JavaSE

1. 自增变量

理解参考博客园博主【叮咚叮咚】的文章 从 i++ 和 ++i 说起局部变量表和操作数栈

public class Main {
    public static void main(String[] args) {
        //两步:先把 int 类型的1 压入到操作数栈,然后再把操作数栈的值赋值给变量 i
        int i = 1;
        // 把i的值1压入到操作数栈中,然后局部变量i自增变为2,最后再把操作数栈的值1赋值给局部变量i,因此此时 i = 1
        i = i++;
        // 把i的值1压入到操作数栈中,然后局部变量i自增变为2,最后再把操作数栈的值1赋值给局部变量j,因此此时 j = 1
        int j = i++;
        //i + ++i * i++     
        // i:把i值2压入到操作数栈中;      操作数栈 值为 2
        // ++i:然后局部变量i先自增变为3,然后再把3压入到操作数栈中; 此时局部变量表中 i= 3;  操作数栈 值为 3
        // i++:把i的值3压入到操作数栈中,然后局部变量i先自增变为4;  操作数栈 值为 3
        // 此时按公式计算操作数栈的值 2+3*3=11,再把11赋值给等号左边的 k; 因此最后 i=4;j=1;k=11
        int k = i + ++i * i++;
        System.out.println("i的值:" + i); // i的值:4
        System.out.println("j的值:" + j); // j的值:1
        System.out.println("k的值:" + k); // k的值:11
    }
}

2. 单例设计模式

设计模式概述

设计模式是在大量的实践中总结理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。

设计模式免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱。"套路"


经典的设计模式一共有23种


单例模式(Singleton):

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,

对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法

要点:
  Ⅰ、某个类只能有一个实例构造器必须私有化

  Ⅱ、这个类必须自行创建这个实例

含有一个该类的静态变量来保存这个唯一的实例

  Ⅲ、必须自行的向整个系统来提供整个实例

  • 对外提供获取该实例对象的方式
  1. 使用public方法直接暴露
  2. 使用静态变量的get方法获取
分类 饿汉式 单例模式 懒汉式 单例模式
优点写法简单,由于内存中较早加载,使用更方便、更快。
是线程安全的。
在需要的时候进行创建,节省内存空间。
缺点内存中占用时间较长线程不安全

① 饿汉式单例模式

理解参考(腾讯云-开发者社区)文章:为什么饿汉式单例是线程安全的?

1、饿汉式单例模式:
	calss	EagerSingleton{
		//1、构造器私有化
		pravite EagerSingleton (){};
		
		//2、在类的内部创建当前类的实例		//4、静态方法中只能调用静态的属性或方法,因此属性也必须声明为私有的
		pravite static EagerSingleton eager = new EagerSingleton();

		//3、提供公共方法返回当前类的实例,必须声明为static的方法
		public static EagerSingleton getInstace(){
			return eager;
		}
	}

/*要点:
 * Ⅰ、某个类只能有一个实例(构造器必须私有化)
 * Ⅱ、这个类必须自行创建这个实例(含有一个该类的静态变量来保存这个唯一的实例)
 * Ⅲ、必须自行的向整个系统来提供整个实例(对外提供获取该实例对象的方式:①使用public方法直接暴露;②使用静态变量的get方法获取)
 *
 * 饿汉式:在类初始化的时候直接创建对象:不存在线程安全问题
 * 1、直接实例化饿汉式(简洁直观)
 * 2、枚举(最简洁)
 * 3、静态代码块(适合复杂实例化)
 *
 * 懒汉式:延迟创建对象
 * 1、线程不安全(适合单线程)
 * 2、线程安全(适合多线程)
 * 3、静态内部类形式(适用于多线程)
 * */
public class HungrySingleton {
    public  static final  HungrySingleton INSTANCE= new HungrySingleton();
    private HungrySingleton() {}


    public static void main(String[] args) {
        //调用饿汉式单例1
        HungrySingleton hungrySingleton = HungrySingleton.INSTANCE;//通过类名直接调用
        System.out.println(hungrySingleton);//HungrySingleton@4eec7777

        //调用饿汉式单例2 枚举本身就是私有化的,枚举重写的toString方法,所以输出的是常量对象的名字
        HungrySingleton2 hungrySingleton2 = HungrySingleton2.INSTANCE;
        System.out.println(hungrySingleton2);//INSTANCE

        //调用饿汉式单例3
        HungrySingleton3 hungrySingleton3 = HungrySingleton3.INSTANCE;
        System.out.println(hungrySingleton3);//HungrySingleton3@404b9385  HungrySingleton3{info='beijing'}

        //调用饿汉式单例4
        System.out.println(HungrySingleton4.getInstance());//HungrySingleton4@3feba861

    }
}

//--------------------------------------------------------------------
/*
 * 枚举:
 *       表示类型的对象是有限的几个,在此我们限定一个,就是单例的了*/
public enum HungrySingleton2 {
    INSTANCE
}
//--------------------------------------------------------------------
/*
* 静态代码块适用于处理复杂的实例化时用
* */
public class HungrySingleton3 {
    public static final HungrySingleton3 INSTANCE;

    static {
        INSTANCE = new HungrySingleton3();
    }

    private HungrySingleton3() {
    }
}
//*******************************************

/*
 * 静态代码块适用于处理复杂的实例化时用;比如需要读取很多初始化文件才能创建实例对象
 * 
 * 配置文件参考下图
 * */

import java.io.IOException;
import java.util.Properties;

public class HungrySingleton3 {
    public static final HungrySingleton3 INSTANCE;
    private String info;


    static {
        try {
            Properties properties = new Properties();
            properties.load(HungrySingleton3.class.getClassLoader().getResourceAsStream("singleInfo.properties"));
            INSTANCE = new HungrySingleton3(properties.getProperty("info"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private HungrySingleton3(String info) {
        this.info=info;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "HungrySingleton3{" +
                "info='" + info + '\'' +
                '}';
    }
}


//--------------------------------------------------------------------

/*
 * 常规例子
 * */
public class HungrySingleton4 {
    //1.构造器私有化
    public HungrySingleton4() {
    }

    //2.创建一个类的实例
    private static final HungrySingleton4 instance = new HungrySingleton4();

    //3.提供get 方法返回这个实例
    public static HungrySingleton4 getInstance() {
        return instance;
    }
}

配置文件

② 懒汉式单例模式

2、 懒汉式单例模式:
	class LazySingleton{
		//1、构造器私有化
		pravite LazySingleton (){};
		
		//2、在类的内部声明当前类的实例		//4、静态方法中只能调用静态的属性或方法,因此属性也必须声明为私有的
		pravite static LazySingleton lazy = null;

		//3、提供公共方法返回当前类的实例,必须声明为static的方法
		public static LazySingleton getInstace(){
			if(lazy == null){
				lazy = new	LazySingleton();
			}
			return lazy;
		}
	}
Ⅰ. 线程不安全,适合于单线程
/*
 *线程不安全,适合于单线程
 * */
public class LazySingleton1 {
    //1.使用静态变量保存唯一实例,必须用private修饰
    // 如果用public修饰的话,外部可以直接通过【类名.INSTANCE】获取到这个实例,拿到的实例可能是null;
    // 如果用private修饰的话,外部只能通过【类名.getInstance方法】获取这个实例,这样就不是null了
    private static LazySingleton1 INSTANCE;

    //2.构造器私有化
    private LazySingleton1() {
    }

    //3.提给外部供获取这个实例的get方法
    public static LazySingleton1 getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new LazySingleton1();
        }
        return INSTANCE;
    }
}

//----------------------------------------------------------------------

public class TestLazySingleton {
    public static void main(String[] args) {
        System.out.println(LazySingleton1.getInstance());//LazySingleton1@3b07d329
    }
}
//----------------------------------------------------------------------


    /*
     * 测试多线程下验证上述写法的问题
     * 
     * 
     * 假如多线程环境下,多个线程会争夺CPU的执行权:
     * 如果第一个线程进来判断对象为null时,开始实例化对象,但是还没有实例化成功的时候,
     * 另一个线程也进来判断对象为null,也开始实例化对象,就会导致两个对象不同
     * */
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable callable = new Callable() {
            @Override
            public LazySingleton1 call() throws Exception {
                return LazySingleton1.getInstance();
            }
        };

        //创建包含2个线程的线程池对象
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Future future1 = executorService.submit(callable);
        Future future2 = executorService.submit(callable);


        LazySingleton1 lazySingleton1 = (LazySingleton1) future1.get();
        LazySingleton1 lazySingleton2 = (LazySingleton1) future2.get();

        System.out.println(lazySingleton1); //LazySingleton1@7ef20235
        System.out.println(lazySingleton2); //LazySingleton1@27d6c5e0
        System.out.println(lazySingleton1 == lazySingleton2); //false
    }

Ⅱ. synchronized 关键字线程安全(适合多线程)
import java.util.concurrent.*;

/*
 *【synchronized 关键字】线程安全【适合多线程】
 *
 *
 * 优缺点说明:
 *          解决了线程不安全问题,
 *          效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。
 *          而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低
 *          结论:在实际开发中,不推荐使用这种方式
 * */
public class LazySingleton2 {
    //1.使用静态变量保存唯一实例,必须用private修饰
    // 如果用public修饰的话,外部可以直接通过【类名.INSTANCE】获取到这个实例,拿到的实例可能是null;
    // 如果用private修饰的话,外部只能通过【类名.getInstance方法】获取这个实例,这样就不是null了
    private static LazySingleton2 INSTANCE;

    //2.构造器私有化
    private LazySingleton2() {
    }

    //3.提给外部供获取这个实例的get方法
    public static LazySingleton2 getInstance() {
        //给 对象上锁
        synchronized (LazySingleton2.class) {
            if (INSTANCE == null) {
                try {
                    //让前面的线程休眠等待第二个线程进来,看是否重新实例化对象
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                INSTANCE = new LazySingleton2();
            }
        }
        return INSTANCE;
    }

//----------------------------------------------------------------------
/*
 *【synchronized 关键字】线程安全【适合多线程】
 *
 * 可以进一步优化,在锁外面先判断实例是否为null
 * */
public class LazySingleton2 {
    //1.使用静态变量保存唯一实例,必须用private修饰
    // 如果用public修饰的话,外部可以直接通过【类名.INSTANCE】获取到这个实例,拿到的实例可能是null;
    // 如果用private修饰的话,外部只能通过【类名.getInstance方法】获取这个实例,这样就不是null了
    private static LazySingleton2 INSTANCE;

    //2.构造器私有化
    private LazySingleton2() {
    }

    //3.提给外部供获取这个实例的get方法
    public static LazySingleton2 getInstance() {
        if (INSTANCE == null) {
            //给 对象上锁
            synchronized (LazySingleton2.class) {
                if (INSTANCE == null) {
                    try {
                        //让前面的线程休眠等待第二个线程进来,看是否重新实例化对象
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    INSTANCE = new LazySingleton2();
                }
            }

        }
        return INSTANCE;
    }

//----------------------------------------------------------------------

public class TestLazySingleton {
    public static void main(String[] args) {
        System.out.println(LazySingleton1.getInstance());//LazySingleton1@3b07d329
        System.out.println(LazySingleton2.getInstance());//LazySingleton2@404b9385
    }
}
//----------------------------------------------------------------------




    /*
     * 测试多线程下验证上述写法的问题
     * 假如多线程环境下,多个线程会争夺CPU的执行权:
     * 如果第一个线程进来判断对象为null时,开始实例化对象,但是还没有实例化成功的时候,
     * 另一个线程也进来判断对象为null,也开始实例化对象,就会导致两个对象不同
     * */
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable callable = new Callable() {
            @Override
            public LazySingleton2 call() throws Exception {
                return LazySingleton2.getInstance();
            }
        };

        //创建包含2个线程的线程池对象
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Future future1 = executorService.submit(callable);
        Future future2 = executorService.submit(callable);


        LazySingleton2 lazySingleton1 = (LazySingleton2) future1.get();
        LazySingleton2 lazySingleton2 = (LazySingleton2) future2.get();

        System.out.println(lazySingleton1); //LazySingleton2@27d6c5e0
        System.out.println(lazySingleton2); //LazySingleton2@27d6c5e0
        System.out.println(lazySingleton1 == lazySingleton2); //true
    }
}

Ⅲ. 静态内部类形式

参考站内博主 houjibofa2050 文章: 类加载器以及常见面试题

import java.util.concurrent.*;

/*
 *静态内部类形式
 *
 * 在内部类被加载和初始化时候才会创建INSTANCE实例对象
 * 静态内部类不会随着外部类的加载和初始化而初始化,它是要单独加载和初始化的
 * 因为实例对象是在内部类中加载和初始化的,因此是线程安全的
 * 
 * */
public class LazySingleton3 {
    //1.构造器私有化
    public LazySingleton3() {
    }

    //2.创建一个内部类,在内部类里面保存这个唯一实例

    private static class Inner{
        private static final LazySingleton3 INSTANCE = new LazySingleton3();
    }

    //3.向外提供get方法
    public static LazySingleton3 getInstance(){
        return Inner.INSTANCE;
    }
}

//----------------------------------------------------------------------
public class TestLazySingleton {
    public static void main(String[] args) {
        System.out.println(LazySingleton1.getInstance());//LazySingleton1@3b07d329
        System.out.println(LazySingleton2.getInstance());//LazySingleton2@404b9385
        System.out.println(LazySingleton3.getInstance());//LazySingleton3@3d075dc0
    }
}
//----------------------------------------------------------------------

Ⅳ. 【双重检查锁(再加volatile关键字)禁止指令重排】线程安全【适合多线程 】(优化版)

在这里插入图片描述

假如多线程环境下,多个线程会争夺CPU的执行权:

如果第一个线程进来判断对象为null时,开始实例化对象,但是还没有实例化成功的时候,另一个线程也进来判断对象为null,也开始实例化对象,就会导致两个对象不同

在这里插入图片描述

import java.util.concurrent.*;

/**
 * @Description :懒汉式:延迟创建这个实例对象
 * (1)构造器私有化
 * (2)用一个静态变量保存这个唯一的实例
 * (3)提供一个静态方法,获取这个实例对象
 * 使用synchronized 关键字
 *【优化版】【双重检查锁(再加volatile关键字)】线程安全【适合多线程 】
 *  如果已经有线程new出来对象,后面的对象没必要在等待
 * @Author : Mr Huang
 * @CreateDate : 2021/1/10 17:22
 * @Version :
 */

public class Singleton06 {

    /**
     * 必须用private修饰,如果用public修饰,直接可以通过
     * 【类名.常量名singleton06】直接访问,拿到的实例可能为 null值;
     * 但是用private修饰的话,只能通过【类名.getInstance方法】来获取这个实例对象
     */

    private static   Singleton06 singleton06;
    private Singleton06(){};
    public static Singleton06 getInstance() {
        if (singleton06 == null){
            synchronized (Singleton06.class) {
                if (singleton06 == null) {
                    singleton06 = new Singleton06();
                }
            }
        }
        return singleton06;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable c = new Callable(){
            @Override
            public Singleton06 call() throws Exception {
                return Singleton06.getInstance();
            }
        };
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future future1 = es.submit(c);
        Future future2 = es.submit(c);
        Singleton06 s1 = (Singleton06) future1.get();
        Singleton06 s2 = (Singleton06) future2.get();
        System.out.println(s1); // Singleton06@27d6c5e0
        System.out.println(s2); // Singleton06@27d6c5e0
        System.out.println(s1 == s2); // true
    }
}

3. 类的初始化和实例化(含代码示例)

类的初始化过程:

  ① 一个类要创建实例需要 先加载并初始化 该类

  main方法所在的类 需要先加载和初始化

  ② 一个子类要初始化需要 先初始化其父类

  ③ 一个类初始化就是执行 clinit 方法

  Ⅰ、 clinit 方法由静态类变量显示赋值代码和静态代码块组成
  Ⅱ、类变量显示赋值代码静态代码块代码从上到下顺序执行
  Ⅲ、clinit 方法只执行一次

实例初始化过程:

  实例初始化就是执行 init 方法

init 方法可能重载多个,有几个构造器就有几个 init 方法

init 方法由非静态实例变量显示赋值代码非静态代码块对应构造器代码组成

③ 非静态实例变量显示赋值代码和非静态代码块从上到下顺序执行,而对于构造器的代码最后执行

④ 每次创建实例对象,调用对应构造器是的,执行的就是对应的 init 方法

init 方法的首行是super()或super(实参列表),即对应父类的 init 方法

方法重写:

1、哪些方法不可被重写
 ① final 方法
 ② 静态 方法
 ③ private 等子类中不可见的方法

2、对象的多样性
 ① 子类如果重写了父类的方法,通过子类对象调用的一定是子类重写的方法
 ②非静态方法默认的调用对象是this
 ③this对象在构造器或者说 init 方法 中就是正在创建的对象

代码理解示例

/**
 * @Description :
 * 父类的初始化:
 * (1)b = method();
 * (2)父类的静态代码块
 *
 * 父类的实例化方法:
 * (1)super()(最前)
 * (2) a = test();
 * (3)父类的非静态代码块
 * (4)父类的无参构造(最后)
 *
 * 非静态方法前面其实有一个默认的对象this
 * this在构造器(或)它表示的是正在创建的对象,因为这里是在创建Son对象,
 * 所以test(()执行的是子类重写的代码(面向对象多态)
 * 这里 a =test()执行的是子类重写的test()方法
 *
 * @Author : Mr Huang
 * @CreateDate : 2021/1/10 21:13
 * @Version :
 *
 */

public class Father {
    private int a = test();
    private static int b = method();

    static {
        System.out.println("1");
    }

    Father() {
        System.out.println("2");
    }

    {
        System.out.println("3");
    }

    public int test() {
        System.out.println("4");
        return 1;
    }

    public static int method() {
        System.out.println("5");
        return 1;
    }
}
//**************************************************
//**************************************************
//**************************************************
/**
 * @Description :
 * @Author : Mr Huang
 * @CreateDate : 2021/1/10 21:13
 * @Version :
 * 子类的初始化:
 * (1)b = method();          5    1
 * (2)子类的静态代码块          10    6
 * 先初始化父类,后初始化子类
 *
 * 子类的实例化方法:
 * (1) super(最前)            9    3   2
 * (2) a = test();            9
 * (3)子类的非静态代码块         8
 * (4)子类的无参构造《最后)      7
 *
 * 因为创建了两个Son对象,因此实例化方法执行两次
 * 9    3   2   9   8  7
 */

public class Son extends Father {
    private int a = test();
    private static int b = method();

    static {
        System.out.println("6");
    }

    Son() {
        //super;写或不写都在,在子类构造器中一定会调用父类的构造器
        System.out.println("7");
    }

    {
        System.out.println("8");
    }

    @Override
    public int test() {
        System.out.print("9");
        return 1;
    }

    public static int method() {
        System.out.println("10");
        return 1;
    }

    public static void main(String[] args) {
        //Father father = new Father();     4   3   2
        Son son1 = new Son();
        System.out.println();
        Son son2 = new Son();
    }
}

4. Java中的参数传递机制

参考站内博主 yuanbinquan 文章: 数组存放位置
参考站内博主 Arya_2 文章: Integer存放位置
参考站内博主 it_withpassion 文章: java对象真的都存储在堆

方法的参数的传递机制:****值传递机制

实参给形参赋值的过程

如果形参是 基本数据类型 的变量,则将实参保存的 数据值 赋给形参。
如果形参是 引用数据类型 的变量,则将实参保存的 地址值 赋给形参。


特殊类型String、包装类 等对象不可变性

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

执行结果:
在这里插入图片描述

不同变量的内存图


在这里插入图片描述

public class Test1 {
    static int s;
    int i;
    int j;

    {
        int i = 1;
        i++;
        j++;
        s++;
    }

    public void test(int j) {
        j++;
        i++;
        s++;
    }

    public static void main(String[] args) {
        Test1 test1 = new Test1();
        Test1 test2 = new Test1();
        test1.test(10);
        test1.test(20);
        test2.test(30);
        System.out.println(test1.i + "    " + test1.j + "    " + test1.s); //执行结果: 2    1    5
        System.out.println(test2.i + "    " + test2.j + "    " + test2.s); //执行结果:1    1    5
    }
}

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值