概述
文章目录
- 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.饿汉式:在类初始化时直接创建实例对象,不管你是否需要这个对象,都会创建。 没有线程安全问题
实现步骤:
- 私有化构造器(private)
- 自行创建,并且用静态变量保存(static)
- 向外提供这个实例 (public)
- 强调这是一个单例,我们可以用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方法 什么时候创建对象)
实现步骤:
- 私有化构造器(private)
- 用一个静态变量保存这个唯一的实例
- 提供一个静态方法,获取这个实例对象
线程不安全:适用于单线程
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 分支相关命令?所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复