Java重温

参考
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. 对外提供方法,让其他程序获取这个对象

步骤

  1. 因为创建对象都需要构造函数初始化,只要将本类中的构造函数私有化,其他程序就无法再创建该类对象
  2. 就在类中创建一个本类的对象
  3. 定义一个方法,返回该对象,让其他程序可以通过方法就得到本类对象(作用:可控)

懒汉

  • 理解
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    1. 为什么叫`懒汉`?
    + 因为懒,所以先不创建对象,暂设为`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
    17
    public class Person
    {
    private static Person person;

    private Person()
    {
    }

    public static Person getInstance()
    {
    if (person == null)
    {
    person = new Person();
    }
    return person;
    }
    }

饿汉

  • 理解
    1
    2
    1. 为什么叫`恶汉`?
    + 先`new`出来,看起来就像一个饥饿的汉子
  • 实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public 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
    12
    1、虚拟机遇到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
    23
    public 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Animal
{

public String animal1="动物1";
public static String animal2="动物2";

{
System.out.println(11);
}

static
{
System.out.println(22);
}

public Animal()
{
System.out.println(33);
}

public void animal_method1()
{
System.out.println(44);
}

public static void animal_method2()
{
System.out.println(55);
}
}
Dog.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Dog extends Animal
{
public String dog1="汪汪1";
public static String dog2="汪汪2";

{
System.out.println(1);
}

static
{
System.out.println(2);
}

public Dog()
{
System.out.println(3);
}

public void dog_method1()
{
System.out.println(4);
}

public static void dog_method2()
{
System.out.println(5);
}
}
TestData.class
1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestData
{
public static void main(String[] args)
{
Dog dog = null;
System.out.println(111);
dog = new Dog();
System.out.println(dog.dog1);
dog.dog_method1();
System.out.println(Dog.dog2);
Dog.dog_method2();
}
}
执行结果
1
2
3
4
5
6
7
8
9
10
11
111
22
2
11
33
1
3
汪汪1
4
汪汪2
5

第一步=null操作只分配空间,接着打印111
因为静态优先与对象存在,在还没new之前先执行静态
又因为父类优先与子类存在,调用子类静态之前先调用父类静态
变量优先于代码块,所以执行了父类静态变量之后执行静态代码块,再执行子类静态变量,子类静态代码块
因为代码块优先于构造函数,所以先执行父类普通变量,然后父类普通代码块,再执行父类构造函数
再执行子类普通变量,然后子类普通代码块,子类构造函数,到此dog = new Dog();执行完毕
后面就是按顺序调用

子类构造函数第一行有一个隐藏的super();表示调用父类的无参构造方法
如果要指定父类构造方法则必须放子类构造方法第一行,因为父类优先于子类而存在
就像是先有爸爸,才能有儿子一个道理

在这里插入图片描述

This

先来个示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ThisTest
{
public static void main(String[] args)
{
Person person = new Person();
System.out.println("main: " + person);
person.print();
}

static class Person
{
void print()
{
System.out.println("this: " + this);
}
}
}
1
2
main: arithmetic.ThisTest$Person@6f94fa3e
this: arithmetic.ThisTest$Person@6f94fa3e
  • 代表对象,就是所在函数所属对象的引用
  • 因为this是对象,所以不能写在静态方法或代码块中
  • 还可以用于构造函数间的调用,调用格式this(实际参数)
  • this对象后面跟上. 调用的是成员属性和成员方法(一般方法)
  • this对象后面跟上() 调用的是本类中的对应参数的构造函数

注意:用this调用构造函数,必须定义在构造函数的第一行.
因为构造函数是用于初始化的,所以初始化动作一定要执行,否则编译失败

JAVA创建对象的顺序和过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Apple a = new Apple("red",200); // 红色的200g苹果1

1,new用到了Apple.class,所以会先找到Apple.class文件并加载进内存中

2,如果有静态(static)代码块,先执行之,静态代码块是给这个类进行初始化的

3,在堆内存中开辟空间,分配内存地址(0X00328F)

4,在堆内存中建立对象的特有属性,并进行默认初始化(例如int类型是默认0.string类型默认为null)

5,对属性进行显示初始化,private boolean isSweet = true(boolean类型默认初始化是false,显示初始化这里是true);

6,如有有构造代码块,执行之,(普通的构造代码块是给对象进行初始化的)

7,执行对应的构造函数

8,将内存地址赋给栈内存中的a变量

native transient assert关键字

  • native修饰方法,
    是java调用一个非java的接口,可以返回类型,可以抛异常
  • transient修饰变量,
    标记为transient的变量,在对象存储时,这些变量状态不会被持久化。
    当对象序列化的保存在存储器上时,不希望有些字段数据被保存,为了保证安全性,可以把这些字段声明为transient。
  • assert
    对一个boolean表达式进行检查,一个正确运行的程序保证boolean值为true;
    若为false,则说明程序不正确,系统需要提供警告信息并且退出程序。
    注意:使用的时候需要在JVM配置参数-ea,或运行jar包使使用java -ea xxx.jar来开启断言,否则无效.