目录
0.静态变量和实例变量的区别
加载的时机不同
静态变量是随着类加载被加载到内存中的,在整个程序运行的过程中只会被加载一次
实例变量会被加载很多次,只要实例化对象就会被加载一次
保存的位置不同
静态变量保存在方法区的静态区中
实例变量随对象一起保存在堆中
访问的方式不同
在本类中:两者都是通过变量名直接访问
在本类范围外,实例变量必须通过对象名来访问,静态变量可以通过对象名访问,但建议通过类名来访问
思考:为什么main()方法必须是静态的呢?
因为main()方法是程序执行的入口,而如果它不是静态方法,需要先创建对象,才能使用它,这就和它是执行的入口矛盾,所以它必须得是静态的
1.什么时候用到 static{} 静态代码块?
有一些代码,需要在所有程序开始运行之前执行,并且只需要被执行一次
2.为什么会用到单例模式?
目前我们使用的java类,都可以创建任意多个对象,实际开发中,我们需要去限制某些类实例化对象的个数,第一,某些类的功能完全可以由同一个对象完成,多次创建会造成内存浪费;第二:某些特殊的任务如果由多个对象完成可能会出现问题。单例模式可以确保某个类的实例是唯一的
3.单例模式的三要点
某个类只能有一个对象
该对象必须是由这个类创建的
提供一个接口供外界获取这个对象
4.单例模式的两种实现
懒汉式(懒加载模式)
只要肚子没饿,就不会找吃的,什么时候饿了,什么时候吃
什么时候需要用到对象,什么时候创建
优点:能够延迟加载,推迟对象占用内存的时间,避免造成不必要的资源浪费
缺点:,每次实例化对象的时候,都需要判断是不是第一次创建,而且在方法中创建实例会面临线程安全的问题
饿汉式(预加载模式)
不管饿不饿,只要饿了,就去找吃的
不管什么时候用到对象,先创建出来
优点:避免了线程不安全问题,不用考虑对象是不是第一次创建
缺点:早早的创建出来,以后不一定会用到,白白的占用内存空间,造成内存浪费
5.final修饰符的使用
又称断子绝孙修饰符
final变量修饰的变量初始化后就不能修改,就变成了常量
final修饰的类不能被继承
final修饰的方法不能被重写
思考:如果访问 final static 修饰的变量会不会触发类的加载
不会
使用static修饰表示它在内存中是独一无二
使用final修饰说明它的值将来不会改变
被它两修饰的类就变成了变量,在内存中会被保存在常量池中,它不会跟类中的别的变量和方法产生任何联系,也不会随类的架加载而加载需要用到它的时候,只需要从常量池中去调用即可。
6.什么时候会触发类的加载?
使用Java命令启动运行的主类
访问一个类的静态变量和静态方法
使用class.forName()尝试获取某个类的镜像对象
创建类对象的时候
思考:什么时候用final?什么时候用static?什么时候用final static?
一个值需要被确定下来不希望被改变,就用final
如果一个对象它所有属性值都相同,就用static
如果一个变量的变量值是唯一的,而且不希望它改变,就用final static
7.抽象类的作用
抽象类和抽象方法的作用就是描述系统功能、定义系统标准和规范。
抽象类不能直接实例化对象,它的主要存在意义就是给子类继承,用来统一描述子类的类型和具有的特征。
抽象类中的抽象方法用来告诉子类,在继承我这个父类的时候,必须要给出哪些方法的实现。
思考:为什么抽象类不能被实例化?
从语义角度上来讲:创建父类对象是没有意义的,父类就是为子类继承而存在的
从语法角度上来讲:创建对象,就可以调用本类的全部方法,而抽象类中的可能有抽象方法,没有方法体,无法执行,两者冲突
8.接口的作用
描述系统具有哪些功能,定义程序的规范
代码的设计依赖于接口,架构师编写一些接口,每个接口中有若干个抽象方法,每个抽象方法,都代表一个具体业务,编码成员通过定义好的接口,去实现具体的业务功能
面向接口编程可以极大程度的提高团队协作的效率
A程序猿实现自已的业务功能时,已经提前知道B程序猿要实现的方法和调用方式,不需要等到B程序猿编写完自己接口具体的实现类结束,再开始编写自己的,实现了并行并发
提高了程序的扩展性和可维护性
传统的开发模式中,模块之间的依赖往往具体到某一个类,当这个类出现问题,产生错误的时候,就会波及到别的模块,导致整个系统瘫痪,而如果我们把对类的依赖改成对接口的依赖,如果这个接口出现问题,把它抛弃,从创建一个即可
接口是实现多继承的重要方法
java中实现多继承的方式:
嵌套继承:如果MyClass类想要同时继承A和B,让A先继承B,然后MyClass在继承A
内部类实现多继承:如果MyClass类想要同时继承A和B,让MyClass类中的两个内部类分别继承A和B
接口实现多继承:一个类只能继承一个父类,但是可以实现多个接口。一个类可以拥有所有它所实现接口的方法
9.接口和抽象类的关系与区别
区别:
抽象类中允许出现抽象方法,也可以出现非抽象方法,而接口中的方法全是抽象方法
抽象类中允许定义局部变量、静态变脸和常量,而接口中的变量都是public final static 修饰的
抽象类中允许出现构造函数,而接口中不允许出现构造方法
一个类可以继承一个类,可以实现多个接口
如果继承抽象父类,子类可以选择性的重写父类的非抽象方法,而如果实现接口,需要实现它所有的方法
联系:两者都不能实例化
都可以定义抽象方法
都可以来定义程序的规范
10.为什么使用内部类
能够让代码实现更好地封装,可以让一个类的访问权限降到最低
有一些类不想被外部类访问,可以把设计为private修饰的内部类,只能有本类访问
使得代码更加具有语义性
有一些类没有必要被其它类访问到,离开本类就没有任何的意义,可以把它设计成内部类
11.内部类的四种类型
静态内部类
使用static修饰的内部类,它相当于类中的静态变量
静态内部类包含的与普通类没有什么区别:静态代码块、匿名代码块、静态变量、实例变量、静态方法、、实例方法等
而且静态内部类本身也可以继承父类或实现接口
虽然静态内部类和外部类的静态成员级别相同,但是类加载的方式不一样,静态内部类采用的是懒加载模式,需要用到内部类的时候,它才会被加载
成员内部类
级别和实例变量相同,相当于外部类的一个实例变量
也就是说成员内部类属于外部类的对象,而不是属于外部类
只有外部类的对象被实例化的时候,成员内部类才会被加载
局部内部类
定义在方法内部,级别相当于局部变量
局部内部类不能用访问修饰符来修饰
当某些功能需要借助内部类来完成,并且这个内部类只需在一个方法中使用,就可以定义成局部内部类,它的生命周期短,而且节省内存,局部内部类的作用范是当前所在代码块{}
匿名内部类
没有名称的内部类,只能临时使用一次
匿名内部类不需要使用class关键字来声明,不能继承父类和实现接口,并且没有构造函数的存在
匿名内部类用于快速定义一个父类的子类和一个接口的实现类
当不需要重复多次地使用某个类的子类,或某个接口的实现类对象的时候,就没有必要专门为这个子类或某个实现类对象定义一个类文件,这时可以使用匿名内部类快速创建一个弗雷德子类或一个接口的实现类
【面试题】==比较和equals()比较有何区别?
注:==:如果是基本数据类型,比较的是数值大小,如果比较的是引用类型,比较的是内存地址
要具体看比较的是什么类型
如果比较的是基本数值类型,那只能用==,因为数值类型不是对象,不能调用方法
如果比较的是引用类型,要看方法调用者是否重写了equals()
如果没有,则是和使用==是一样的,因为Object源码中,equals()方法其实返回的就是==的比较结果
r如果重写了equals()方法,则比较的是它们具体的内容
对象调用了equals(),并且传入的参数是String字符串,只有它们的内容,大小写,顺序完全相同是才会返回true,否则返回false
12.String字符串地址的规则
JVM在内存中为String类维护了一个常量池(String Constant Pool),便于相同字符串对象的重复利用,节省内存空间。
如果是使用字面量定义的字符串,JVM会先去常量池中看是否已经存在相同的字符串
如果不存在,则创建该字符串放到常量池中,并且返回其引用
也就是说,通过自变量定义的相同内容的字符串,实际上,内存地址都是相同的
如果使用new关键字创建出来的对象,会在堆中无条件的开辟一块内存,创建一个String对象
也就是说,所有通过new出来的String对象,内存地址都不一样
13.八种基本数据类型对应的包装类
基本类型 | 包装类型 |
boolean | Boolean |
byte | Byte |
char | Character |
short | Short |
int | Integer |
float | Float |
double | Double |
long | Long |
14.装箱的两种方法
通过包装类的构造器实现装箱
byte b=10;
Byte bobj=new Byte(b);
通过valueOf()实现装箱
byte b=10;
Byte bobj=new Byte(b);
15.构造器和valueOf()实现装箱的联系和区别
valueOf()实际上就是通过new包装类对象实现的包装,只不过和new一个包装类对象还是有一定区别的,valueOf会利用底层的缓存机制
16.为什么要拆箱
基本数据类型能够减少系统的开销
基本数据类型方便进行算数运算
17.拆箱的实现方法,以及实现原理
直接让包装类对象调用对应的xxxValue()方法,返回值就是对应的基本类型值
实现原理:在每一个包装类里,都有一个基本类型的属性value,用来保存真正的数值大小,当需要拆箱时,只需要调用×××value(),就会直接返回value的属性值