我是靠谱客的博主 小巧灯泡,最近开发中收集的这篇文章主要介绍Day01_尚硅谷面试题第一季01.自增变量02.单例设计模式03.类的加载过程04.方法的参数传递机制05.递归与迭代06.成员变量与局部变量13.Git 分支相关命令?,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

  • 01.自增变量
      • 1)、解析:i = i++
      • 2)、解析:int j = i++
      • 3)、解析:int k = i + ++i * i++
      • 4)、小结
      • 5)、另说
  • 02.单例设计模式
      • 1.饿汉式:在类初始化时直接创建实例对象,不管你是否需要这个对象,都会创建。 **没有线程安全问题**
      • 2.懒汉式:延迟创建这个实例对象 (什么时候调用get方法 什么时候创建对象)
      • 3.面试题:单例模式的线程安全性
  • 03.类的加载过程
      • 1.面试题
      • 2.方法的重写 Override
  • 04.方法的参数传递机制
  • 05.递归与迭代
  • 06.成员变量与局部变量
      • 1.基本知识
      • 2.执行流程
  • 13.Git 分支相关命令?


JavaSE部分

01.自增变量

在这里插入图片描述

1)、解析:i = i++

1、 执行过程
① 把 i 的值压入操作数栈
② i 变量自增1(重点:自增1是在局部变量表中进行的!)
③ 把操作数栈中的值赋值给 i(重点:赋值过程为操作数栈的值覆盖局部变量表中的值的过程???!!!)

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

2、字节码角度分析

在这里插入图片描述

3、细节说明:i=i++

  • 同时在局部变量表和操作数栈中存在 i 。
  • i++ 是在局部变量表中执行的!!!!
  • 赋值操作会使得操作数栈的 i 值,覆盖局部变量表中的 i 值。(关键点:覆盖)
  • 另外注意:i ++ 是用后加

2)、解析:int j = i++

1、 执行过程
① 把 i 的值压入操作数栈
② i 变量自增1
③ 把操作数栈中的值赋值给 j

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

2、字节码角度分析

在这里插入图片描述

3)、解析:int k = i + ++i * i++

1、 执行过程
① 把 i 的值压入操作数栈
② i 变量自增1
③ 把 i 的值压入操作数栈
④ 把 i 的值压入操作数栈
⑤ i 变量自增
⑥ 把操作数栈中前两个弹出求乘积结果再压入栈
⑦ 把操作数栈中的值弹出求和再赋值给 k

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

2、字节码角度分析

在这里插入图片描述

3、从字节码角度理解“i++ 与 ++i 的区别”
个人理解:iload(压入操作数栈)和 innc(局部变量表中自增)的先后顺序不同。

4)、小结

在这里插入图片描述

5)、另说

i++++i里面还有一套规则就是:如果是【前++】,那么【先加后用】;如果是【后++】,那么【先用后加】
在基本的操作里面(像int j = i++)很适合使用这套规则,但是遇到i=i++int k = i + ++i * i++这种复杂的式子里面还是适合使用操作数栈的方式解释。

/*
自增运算符:++
自减运算符:--

1. 在单独使用的时候,前++和后++没有任何区别。也就是:++num;和num++;是完全一样的。
2. 在混合的时候,有【重大区别】
	A. 如果是【前++】,那么变量【立刻马上+1】,然后拿着结果进行使用。	【先加后用】
	B. 如果是【后++】,那么首先使用变量本来的数值,【然后再让变量+1】。	【先用后加】

注意事项:
	只有变量才能使用自增、自减运算符。常量不可发生改变,所以不能用。
*/

		int num1 = 10;
		System.out.println(num1); // 10
		++num1; // 单独使用,前++
		System.out.println(num1); // 11
		num1++; // 单独使用,后++
		System.out.println(num1); // 12
		
		// 与打印操作混合的时候
		int num2 = 20;
		// 混合使用,先++,变量立刻马上变成21,然后打印结果21
		System.out.println(++num2); // 21
		System.out.println(num2); // 21
		System.out.println("=================");
		
		int num3 = 30;
		// 混合使用,后++,首先使用变量本来的30,然后再让变量+1得到31
		System.out.println(num3++); // 30
		System.out.println(num3); // 31
		System.out.println("=================");
		
		int num4 = 40;
		// 和赋值操作混合
		int result1 = --num4; // 混合使用,前--,变量立刻马上-1变成39,然后将结果39交给result1变量
		System.out.println(result1); // 39
		System.out.println(num4); // 39
		System.out.println("=================");
		
		int num5 = 50;
		// 混合使用,后--,首先把本来的数字50交给result2,然后我自己再-1变成49
		int result2 = num5--;
		System.out.println(result2); // 50
		System.out.println(num5); // 49
		System.out.println("=================");
		
		int x = 10;
		int y = 20;
		// 11 + 20 = 31
		int result3 = ++x + y--;
		System.out.println(result3); // 31
		System.out.println(x); // 11
		System.out.println(y); // 19
		
		// 30++; // 错误写法!常量不可以使用++或者--

02.单例设计模式

这里是引用

不论是饿汉式还是懒汉式都有一个共同特点:类是public修饰的,而且类里面只有一个public,其余的都是private

1.饿汉式:在类初始化时直接创建实例对象,不管你是否需要这个对象,都会创建。 没有线程安全问题

实现步骤:

  1. 私有化构造器(private)
  2. 自行创建,并且用静态变量保存(static)
  3. 向外提供这个实例 (public)
  4. 强调这是一个单例,我们可以用final修饰
//方法一:最朴素的方式

public class Singleton {
    public static final Singleton INSTANCE = new Singleton();
   
    private Singleton() {};
}
//方法二,使用枚举类型,枚举类型表示该类型的对象是有限的几个。我们可以限定为一个,就成了单例

public enum Singleton2 {
    INSTANCE;
}
//方法三:使用静态代码块 实现复杂逻辑构造器的饿汉式单例模式

public class Singleton3 {
	//私有化有参构造器
    private Singleton3(String info){
        this.info = info;
    }
    private String info;
    public static final Singleton3 INSTANCE;
    //在静态代码块中进行初始化实例对象
    static {
        Properties props = new Properties();
        try {
            props.load(Singleton3.class.getClassLoader().getResourceAsStream("application.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        String info = props.getProperty("info");
        INSTANCE = new Singleton3(info);
    }

}

2.懒汉式:延迟创建这个实例对象 (什么时候调用get方法 什么时候创建对象)

实现步骤:

  1. 私有化构造器(private)
  2. 用一个静态变量保存这个唯一的实例
  3. 提供一个静态方法,获取这个实例对象

线程不安全:适用于单线程

public class Singleton4 {
    //私有化构造器
    private Singleton4() {
    }

    //私有化静态变量
    private static Singleton4 INSTANCE;

    /**
     * 有线程安全问题
     */
    public static Singleton4 getInstance() {
        //TODO 线程不安全
        if (INSTANCE == null) {
            INSTANCE = new Singleton4();
        }
        return INSTANCE;
    }
}

线程安全:适用于多线程

public class Singleton5 {
    private Singleton5(){}
    
    private static Singleton5 instance;
    
    public static Singleton5 getInstance(){
    	//提高效率
        if(instance == null){
			//增加同步锁
            synchronized (Singleton5.class){
            	//双端验证(为什么使用双端校验?查看“尚硅谷面试题第二季”)
                if(instance == null){
                    instance = new Singleton5();
                }
            }
        }
        return instance;
    }
}

线程安全:使用静态内部类实现的 线程安全

静态内部类不会自动随着外部类的加载和初始化而初始化,(他虽然在类的内部)但他还是需要单独去加载和初始化的
因为是在内部类加载和初始化时创建的,因此是线程安全的

public class Singleton6 {

    private Singleton6(){}
    
    public static Singleton6 getInstance(){
        return Inner.instance;
    }
    
    //静态内部类
    private static class Inner{
        private static final Singleton6 instance = new Singleton6();
    }
}
  • 总结:
    什么时候使用饿汉式 / 懒汉式,取决于需要什么时机来创建对象。
  • 关于线程安全问题:
    只要是饿汉式,都没有线程安全问题。
    懒汉式,有可能存在线程安全问题,但是可以通过同步锁或静态内部类解决。

3.面试题:单例模式的线程安全性

1)饿汉式单例模式的写法:线程安全
2)懒汉式单例模式的写法:非线程安全
3)双检锁单例模式的写法:线程安全

03.类的加载过程

1.面试题

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

第一步:先进行类初始化

一个类要创建实例需要先加载并初始化该类,而main方法所在的类需要先加载和初始化,所以先初始化有main方法的子类。
但是一个子类要初始化需要先初始化父类。

  • 一个类初始化就是执行<clinit>()方法
    <clinit>()方法由(静态类变量显示赋值代码)和(静态代码块)组成
    ◆(静态类变量显示赋值代码)和(静态代码块)代码从上到下顺序执行

所以,先执行(5),因为(5)是父类的“静态类变量显示赋值代码”;
然后执行(1),因为(1)是父类的“静态代码块”;
然后执行(10),因为(10)是子类的“静态类变量显示赋值代码”;
然后执行(6),因为(6)是子类的“静态代码块”;
总结就是(5)(1)(10)(6)

第二步:执行main方法里的Son s1 = new Son();

  • 这就是要执行实例初始化了,实例初始化过程:

    • 执行实例初始化就是执行<init>()方法
    • <init>()方法由(非静态实例变量显示赋值代码)和(非静态代码块)还有(对应构造器代码)组成
    • (非静态实例变量显示赋值代码)和(非静态代码块)从上到下顺序执行,而(对应构造器代码)最后执行
    • 执行<init>()方法之前务必先执行 super()super(实参列表),即对应父类的<init>()方法

所以执行son的<init>()方法之前先执行super(),也就是先执行父类的<init>()方法
(1)父类super()(没有)
(2)父类的非静态实例变量显示赋值代码 ——i = test();(因为涉及到重写问题,这里输出的是(9)而不是(4))
(3)父类的非静态代码块(3)
(4)父类的无参构造(2)
然后执行子类的非静态实例变量显示赋值代码 ——i = test();,输出(9)
然后子类的非静态代码块(8)
然后父类的无参构造(7)

总结就是Son s1 = new Son();的执行会依次输出(9)(3)(2)(9)(8)(7)

第三步:执行main方法里的System.out.println();

第四步:执行main方法里的Son s1 = new Son();
Son s1 = new Son();的执行会依次输出(9)(3)(2)(9)(8)(7)

总结:

  • 父类静态代码块/静态类变量显示赋值代码 =》 子类静态代码块/静态类变量显示赋值代码 =》 父类非静态代码块/非静态实例变量显示赋值代码 =》 父类构造器 =》 子类非静态代码块/非静态实例变量显示赋值代码 =》 子类构造器
  • (5)(1)(10)(6) (9)(3)(2)(9)(8)(7)

2.方法的重写 Override

  • 哪些方法不可以被重写

    • final方法
    • 静态方法(父类的method()方法是静态方法,所以没有被子类重写了)
    • private等子类中不可见方法

巧计:所以说当你遇到“private static final int m;”时,这个m相当了不得,是绝对不能被重写的

  • this相关
    • 非静态方法前面其实有一个默认的对象 this
    • this 在构造器(或)它表示的是正在创建的对象。
    • Son s1 = new Son();可知,因为这里是在创建 Son 对象,所以 test() 执行的是子类重写的代码(面向对象多态),这里 i=test() 执行的是子类重写的 test() 方法。

04.方法的参数传递机制

在这里插入图片描述

运行结果:
i = 1
str = helloworld (错误!正确答案:hello)
num = 3 (错误!正确答案:2)
arr = 2,2,3,4,5(不是{1,2,3,4,5}哦)
my.a = 11

回顾JVM中的一幅图

这是JVM中的一幅图,我们说过每个栈帧对应一个方法
在这里插入图片描述

解析

这里是引用
在这里插入图片描述
String、包装类对象的不可变性,如果要改变就会产生新的对象
在这里插入图片描述
在这里插入图片描述

知识总结

这里是引用

05.递归与迭代

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

06.成员变量与局部变量

1.基本知识

区分成员变量、局部变量、类变量、实例变量

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

所在的位置

在这里插入图片描述

堆里面存储实例变量;
栈里面存储局部变量;
方法区里面存储不太变化东西,比如类变量(静态变量)、常量、类信息等

巧计:
类变量在方法区里面存储应该很好记——方法区里面存储不太变化的变量
局长(局部变量在栈里面)
石堆(实例变量在堆里面)

在这里插入图片描述

作用域

在这里插入图片描述

访问方式

在这里插入图片描述
注意:我们都知道静态的会在类加载之前就加载进来,所以“静态的不能访问非静态的”,但千万注意,“静态的不能访问非静态的”这句话不是说静态的不能调用非静态的东西,比如说我这个main方法是静态的它不能调用非静态的test()方法吗?肯定不是!而是必须要使用obj1.test()的方式去调用。所以说并不是因为我在你前面就加载了所以我就不能调用你,不存在这种事情啊。只是调用方式要注意。

默认值问题
局部变量没有默认值,成员变量有默认值0

2.执行流程

从12:00开始看即可

Exam5 obj1 = new Exam5();执行时发生如下:

在这里插入图片描述

Exam5 obj2 = new Exam5();执行时发生如下:

这里是引用

obj1.test(10);执行时发生如下:

在这里插入图片描述

obj1.test(20);执行时发生如下:

这里是引用

obj2.test(30);执行时发生如下:

这里是引用

SSM部分

13.Git 分支相关命令?

参考文章:Day20_Git—Git常用的操作

分支相关命令是最容易被问到的

1、创建分支

git branch <分支名>
git branch -v 查看分支

2、切换分支

git checkout <分支名>

3、创建并切换分支
平常是先创建a分支然后切换到a分支,现在一步搞定

git checkout -b <分支名> 

4、合并子分支

先切换到主干 git checkout master
git merge <分支名>

5、删除分支

先切换到主干 git checkout master
git branch -D <分支名>

6、工作流介绍:
(产品上线的是master分支,开发人员开发的是develop分支,一个开发人员负责的往往是dev的子分支feature_goldstyle或feature_game)
简单来说就是 master 分支上线,如果 master 出问题,会创建一个 hotfix 分支进行解决 bug ,解决完后合并到 master 分支和 develop 分支,保持一个同步;
有新的分支开发完成(feature_goldstyle或feature_game)就会和 develop 分支合并,然后创建一个 release 分支进行测试,完成后在合并到 master 和 develp ,保持一致。
在这里插入图片描述

最后

以上就是小巧灯泡为你收集整理的Day01_尚硅谷面试题第一季01.自增变量02.单例设计模式03.类的加载过程04.方法的参数传递机制05.递归与迭代06.成员变量与局部变量13.Git 分支相关命令?的全部内容,希望文章能够帮你解决Day01_尚硅谷面试题第一季01.自增变量02.单例设计模式03.类的加载过程04.方法的参数传递机制05.递归与迭代06.成员变量与局部变量13.Git 分支相关命令?所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部