参考
https://blog.csdn.net/xyzopq100/article/details/50683899
https://blog.csdn.net/myz512/article/details/94851180
&与&&区别
&
无论左边结果是什么,右边都计算&&
如果左边结果是false,右边不计算
|与||区别
|
无论左边结果是什么,右边都计算||
如果左边结果是true,右边不计算
>>与>>>区别
break,continue,return
break;
跳出当前循环continue;
继续循环,结束本次循环(只作用于循环结构)return;
结束为void
的方法;return x;
结束非void
的方法并返回一个值.
单例模式
思想
- 不让其他程序创建该类对象
- 在本类中创建一个本类对象
- 对外提供方法,让其他程序获取这个对象
步骤
- 因为创建对象都需要构造函数初始化,只要将本类中的构造函数私有化,其他程序就无法再创建该类对象
- 就在类中创建一个本类的对象
- 定义一个方法,返回该对象,让其他程序可以通过方法就得到本类对象(作用:可控)
懒汉
- 理解
1
2
3
4
5
6
7
8
9
10
111. 为什么叫`懒汉`?
+ 因为懒,所以先不创建对象,暂设为`null`,就好像懒得做作业一样,最后再做
2. 为什么给对象设置`private`?
+ 防止其他类直接使用`Person.person`来使用静态对象
3. 为什么给对象设置`static`?
+ 因为它需要被静态方法`getInstance()`调用,不设置`static`的话,调用不了【静态优先对象存在】
4. 为什么给无参构造器设置`private`?
+ 设置`private`之后,其他类就不能`new`对象了
5. 为什么对方法`getInstance()`设置`public` `static`?
+ 设置`public`就可以被其他类访问
+ 设置`static`就可以直接使用`Person.getInstance()`来获取实例对象 - 实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class Person
{
private static Person person;
private Person()
{
}
public static Person getInstance()
{
if (person == null)
{
person = new Person();
}
return person;
}
}
饿汉
- 理解
1
21. 为什么叫`恶汉`?
+ 先`new`出来,看起来就像一个饥饿的汉子 - 实现
1
2
3
4
5
6
7
8
9
10
11
12
13public class Person
{
private static Person person = new Person();
private Person()
{
}
public static Person getInstance()
{
return person;
}
}
双重检验锁
可参考
类加载过程与初始化
synchronized与volatile
- 对象的创建过程
person = new Person();
1
2
3
4
5
6
7
8
9
10
11
121、虚拟机遇到new指令,到常量池定位到这个类的符号引用。
2、检查符号引用代表的类是否被加载、解析、初始化过。
3、虚拟机为对象分配内存。
4、虚拟机将分配到的内存空间都初始化为零值。
5、虚拟机对对象进行必要的设置。
6、执行方法,成员变量进行初始化。
7、将对象的引用指向这个内存区域。
简化步骤:
a、【开辟空间】JVM为对象分配一块内存M
b、【构造对象】在内存M上为对象进行初始化
c、【把地址值赋给变量】将内存M的地址复制给person变量 - 理解
1
2
3
4
5
6双重检验锁:又简称为DCL 全称Double Check Lock
它是基于懒汉式单例的改造。下面我们来看改造过程。
1. 首先在getInstance方法中的内层if外面加了synchronized锁,使得线程同步,这就不多说了。
2. 此时,无论对象是否为null,都会进同步代码块,所以synchronized外面加了if判断,来提供效率。
3. 在这里,如果变量不加volatile进行修饰,会造成步骤b和c指令重排序,提取把地址值赋给了变量。因此,加了volatile - 实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class Person
{
private static volatile Person person;
private Person()
{
}
public static Person getInstance()
{
if (person == null)
{
synchronized (person)
{
if (person == null)
{
person = new Person();
}
}
}
return person;
}
}
执行顺序
Animal.class
1 | public class Animal |
Dog.class
1 | public class Dog extends Animal |
TestData.class
1 | public class TestData |
执行结果
1 | 111 |
第一步
=null
操作只分配空间,接着打印111
因为静态优先与对象存在,在还没new
之前先执行静态
又因为父类优先与子类存在,调用子类静态之前先调用父类静态
变量优先于代码块,所以执行了父类静态变量之后执行静态代码块,再执行子类静态变量,子类静态代码块
因为代码块优先于构造函数,所以先执行父类普通变量,然后父类普通代码块,再执行父类构造函数
再执行子类普通变量,然后子类普通代码块,子类构造函数,到此dog = new Dog();
执行完毕
后面就是按顺序调用
子类构造函数第一行有一个隐藏的
super();
表示调用父类的无参构造方法
如果要指定父类构造方法则必须放子类构造方法第一行,因为父类优先于子类而存在
就像是先有爸爸,才能有儿子
一个道理
This
先来个示例
1 | public class ThisTest |
1 | main: arithmetic.ThisTest$Person@6f94fa3e |
- 代表对象,就是所在函数所属对象的引用
- 因为this是对象,所以不能写在静态方法或代码块中
- 还可以用于构造函数间的调用,调用格式
this(实际参数)
- this对象后面跟上
.
调用的是成员属性和成员方法(一般方法) - this对象后面跟上
()
调用的是本类中的对应参数的构造函数
注意
:用this调用构造函数,必须定义在构造函数的第一行.
因为构造函数是用于初始化的,所以初始化动作一定要执行,否则编译失败
JAVA创建对象的顺序和过程
1 | Apple a = new Apple("red",200); // 红色的200g苹果1 |
native transient assert关键字
native
修饰方法,
是java调用一个非java的接口,可以返回类型,可以抛异常transient
修饰变量,
标记为transient的变量,在对象存储时,这些变量状态不会被持久化。
当对象序列化的保存在存储器上时,不希望有些字段数据被保存,为了保证安全性,可以把这些字段声明为transient。assert
对一个boolean表达式进行检查,一个正确运行的程序保证boolean值为true;
若为false,则说明程序不正确,系统需要提供警告信息并且退出程序。
注意:使用的时候需要在JVM配置参数-ea
,或运行jar包使使用java -ea xxx.jar
来开启断言,否则无效.