记录JAVA中最基础,但不主动研究,容易忽略的知识点
目录
String,StringBuffer,StringBuilder
类
初始化执行顺序
- 父类静态变量
- 父类静态代码块
- 子类静态变量
- 子类静态代码块
- 父类成员变量
- 父类成员代码块
- 父类构造函数
- 子类成员变量
- 子类成员代码块
- 子类构造函数
JAVA文件多个类
- 一个Java文件中可以定义多个类,但是最多只能有一个类被public修饰,并且这个类的类名与文件名必须相同。
- 若这个文件中没有public的类,则文件名随便是一个类的名字即可。
标识接口
没有任何方法申明的接口叫做标识接口(Serializable、Cloneable),充当一个标识的作用。
内部类
静态内部类(不依赖外部类实例化),成员内部类(依赖外部类实例化),局部内部类,匿名内部类
getClass()
Object类的方法,子类无法覆盖,释义:返回此Object运行时类
final
final指的是引用不可变
finalize()
Object类的方法,GC执行时会调用被回收对象的finalize()方法,可以覆盖此方法实现对其他资源的回收(回收时会先调用finalize(),并在下一次回收时真正释放内存)
static
被static修饰的方法不能使用this,super
switch
不管是byte,short,char,还是String(java7支持,hashcode()获取int值),都是被转换成int后做的比较
volatile
被volatile修饰的变量,会直接从主内存中取值,不会使用线程自己的本地内存(缓存)。volatile不能保证操作原子性。
strictfp
strict float point的缩写,指精确浮点,被strictfp修饰的类,浮点计算会按照IEEE754的标准实现。
null
不是Object实例,没有分配内存,表明引用没有指向任何对象。
不可变类
当不可变类被实例化后,对象中的所有成员变量不可修改。所有的包装类型都是不可变类
原则:
- 所有成员变量被private修饰
- 没有写或修改成员变量的方法,只提供构造函数
- 类中方法不会不会被子类覆盖,final实现
- 如果一个类成员变量本身是不可变量,在初始化或获取这个成员变量时,使用clone()方法来确保不可变
面向对象
基本特征
- 抽象:忽略一个主题与当前目标无关的方面,更充分关注到有关的方面(忽略啄木鸟和鹦鹉喙的长短不同,关注都是有喙的,以此抽象出喙,颜色的特征),有过程抽象(行为:都会飞,跑),数据抽象(特征:都有羽毛)。
- 继承:继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。
- 封装:指将客观事物抽象成类(通过不同鸟的共同特征,封装成Bird类)。
- 多态:同一个行为具有多个不同表现形式或形态的能力(同样是+号,3+4实现整数相加,"3"+"4"实现字符串拼接)。
扩展:【组合】,组合和继承都是代码服用的方式,组合是通过再新类中创建原有类的对象,实现服用。
多态实现机制
- 方法重载,由于重载的参数不同,因此编译时即可确定调用的方法。【编译时多态】
- 方法重写,基类的引用可以指向子类实例对象,接口引用变量可以指向其实现类实例对象,程序调用方法在运行期才动态绑定。【运行时多态】
只有类中的方法才有多态的概念,成员变量没有多态概念,成员变量的取值取决于定义的变量类型,不取决于创建的对象类型。
数据传递
基本数据类型是值传递,其余类型都是引用传递(引用传递的本质也是值传递,只不过传递的值是对象的地址)
其他
Math中的round()、ceil()和floor()
round():四舍五入,原理是数字加0.5再向下取整
ceil():向上取整,取大于目标数的最小整数值
floor():向下取整,取小于目标数的最大整数值
String
String是不可变类
存储方式
若字符串常量区有指定字符串则直接获取引用,没有则创建。
String s = "abc"; // "abc"放到字符串常量区,s指向它
String s = "a" + "bc" // 转化成"abc"后再放到常量区
s = "def"; // 由于String是不可变类,所以"def"放到字符串常量区,s重新指向它、
String s = new String("abc"); // 在堆中创建一个String对象指向常量区里的"abc"
new String("abc")会创建一个或两个对象,一个是堆里的对象,一个是字符串常量区的对象(不存在时)
==,equals(),hashCode()
- ==:比较变量对应内存中所存储的值是否相等。因此,引用类型变量里存放的是其对象的地址,仅能比较是否指向同一对象,不能比较内容是否相等。
- equals():Object类的方法,默认就是==,可被覆盖。String类重写了equals,用于比较对象中的内容是否相等
String s1 = new String("abc");
String s2 = new String("abc");
s1 == s2; // false,变量中的对象地址不同
s1.eqauls(s2); // true
- hashCode():Object类的方法,将对象地址转化为一个int,可被覆盖。如果不重写,任何两个对象的hashCode()都不等
String,StringBuffer,StringBuilder
- String:不可变类,适合字符串被大量共享的场景使用
- StringBuffer:可变类,多线程字符串需要经常被修改时使用,线程安全
- StringBuilder:可变类,单线程字符串需要经常被修改时使用,线程不安全
效率
String<StringBuffer<StringBuilder
String作为不可变类的修改操作是基于StringBuffer实现
String s = "hello";
s += "world";
// 等价
StringBuffer sb = new StringBuffer(s);
sb.append("hello");
s = sb.toString();
数组
二维数组第二维长度可以不一样
异常处理
finally
- finally块中的代码在try和catch块的return之前执行
- finally块中的return会覆盖try和catch块的return
引申:由于在一个方法内部定义的变量都存储在栈中,当这个函数结束后,其对应的栈就会被回收,此时在其方法体中定义的变量将不存在了,因此return在返回时不是直接返回变量的值,而是复制一份,然后返回。所以程序return时会先将返回值存到指定的位置,再执行finally,所以再finally中修改返回值,对基本类型无效,对引用类型有效。
public class Test {
public static void main(String[] args) {
System.out.println(Test.returnBase()); // 1
System.out.println(Test.returnString()); // hello world
}
public static int returnBase() {
int i = 1;
try {
return i;
} catch (Exception e) {
return 0;
} finally {
i++;
}
}
public static StringBuffer returnString() {
StringBuffer i = new StringBuffer("hello");
try {
return i;
} catch (Exception e) {
return null;
} finally {
i.append(" world");
}
}
}
异常
所有异常都继承自(java.lang.Throwable)
Erorr
Erorr表示程序在运行期间出现了严重错误,不可恢复,会导致程序停止(如:OutOfMemoryError)
Exception
可恢复异常,编译器能检查到。有两种
- 检查异常:所有继承自Exception并且不是运行时异常的异常都是检查异常(IOException,SQLException),编译器会强制在代码中捕获此异常
- 运行时异常:编译器不会强制捕获,出现运行时异常时,系统会一直把异常向上抛,由JVM处理(NullPointerException,ClassCastException)
IO流
- 字节流以字节为单位(8bit),不使用缓存
- 字符流以字符为单位(16bit),使用缓存
JAVAIO类使用了装饰者模式
NIO
JAVA传统通信使用socket,accept和read都有可能会发生阻塞,如果有多个连接,就要用到多线程,每个线程有自己的栈空间,而且由于阻塞会导致大量线程进行上下文切换,使得程序的运行效率非常低下。
NIO(非阻塞IO):Channel,Selector,Buffer来实现
- Channel:双向(可读可写)的通道
- Selector:管理Channel,实现时会把Channel的IO事件注册到Selector中,Selector轮询已注册的Channel,发现某个Channel有事件发生,传回Selectionkey(与Channel对应的key),通知程序对对应Channel进行读写
- Buffer:存放Channel读写的数据
由于轮询比线程的切换效率高,所以并发下NIO执行效率更高
序列化
序列化将对象转化为字节的过程
static,transient修饰的变量不会被序列化
serialVersionUID
用于判断对象是否一致
最好显式声明:
- 提高程序运行效率:不声明,系统有自动计算过程
- 提高不同平台兼容性:不同平台的JVM,serialVersionUID生成方式不一样,有可能一个平台序列化的对象不能再另一平台反序列化
- 提高程序不同版本兼容性:修改类的属性后,计算出的serialVersionUID不一致
外部序列化
实现Externalizable接口,重写方法自定义实现序列化操作。
JAVA平台与内存管理
class执行的两种方式
- 即时编译:先将字节码编译成机器码,再执行机器码
- 解释执行:解释器每次解释并执行一小段代码(通常采用)
加载class文件的方式
隐式加载:new等操作时,隐式调用类加载器将类加载到JVM
显式加载:Class.forName
类加载器
BootstrapClassLoader:加载系统类(jre/lib/rt.jar)
ExtClassLoader:加载扩展类(jar/lib/ext/*.jar)
AppClassLoader:加载应用类
双亲委派机制
三个类加载器是协作完成类加载的,具体的实现方式是
- 有类需要加载时,类加载器会委托父类进行类加载
- 父类按照自己的搜索路径查找需要加载的类,搜索不到,交给子类自己加载
好处
- 防止重复加载:通过委托,类已经被父类加载过了,就不用再加载。
- 防止系统级别的类被篡改:系统类一定是被BootstrapClassLoader按自己的路径加载过了,所以自己写一个系统类也不会被加载。
PS:双亲委派其实是并不准确,并不是指有一个父class和母class,java也没有多继承,看网上的看法可能是翻译问题:parents delegate