Java基础知识
本文内容全来自于:www.cyc2018.xyz/Java/Java%2…
一、数据类型
1、基本类型
java有8大基本类型,char、byte、short、int、float、long、double、boolean,分别对应的字节是1、2、2、4、4、8、8、~字节,1字节等于8bit,所以也分别对应8、16、16、32、32、64、64、~bit。在java规范中,boolean没有明确指出具体的大小,但有如下说法,因为boolean只有true和false,在编译时用0和1表示,所以只需要占用1bit来进行存储。
2、包装类型
基本类型都有对应的包装类型,Character、Byte、Short、Integer、Float、Long、Double、Boolean,在使用时能根据需要进行自动拆箱或自动装箱,能将将对象伪装成基本类型的值或基本类型的值伪装成对象。比如:
Integer x = 2; // 自动装箱,调用了Integer.valueOf(x);
int y = x; //自动拆箱,调用了x.intvalue();
3、缓存池 boolean、byte、char、short、int都有缓存池,如果对应的包装类在调用对应的valueOf方法或者自动装箱时,数值如果在缓冲池的范围内,就会直接返回缓冲池中的对象,否则就会创建一个新对象去存储。 如下是基本类型对应的缓冲池范围:
- boolean values true and false
- all byte values(0~255)
- char in the range \u0000 to \u007F
- short values between -128 and 127
- int values between -128 and 127
二、String
String类是被final修饰的,因此不可被继承。 在jdk1.8中,string采用了char数组存储字符,char数组用了final修饰,因此string类型的变量是不可变的。
不可变的好处:
1、用于map的key,因为不可变的特性会让hash不需要重复计算。
2、String类型的常量池需要。
3、安全性,比如网路连接参数用String类型,若可变,可能会导致连接出现安全问题,但String不可变就能尽量避免这类问题。
4、线程安全,因为不可变,所以对于线程来说是非常安全的。
String、StringBuffer、StringBuidler
1、String是不可变的。
2、StringBuffer和StringBuilder是可变的。
3、String和StringBuffer是线程安全的,StringBuffer内部使用synchronized进行同步。
4、StringBuilder是线程不安全的。
String常量池
String s = new String("aaa");
String s = "aaa";
上面2行代码的区别在于,new String的时候会创建一个对象,若常量池中不存在和当前创建的对象的字符串字面量相同的对象时,就会再创建一个对象放入常量池,若存在,则不会创建。 而直接赋值就和上面的后文相同,若不存在就创建,存在则不创建。
三、运算
Java不能隐式执行向下转型,因为这样会降低精度。 比如1.1字面量属于double类型,1.1f是float类型,不能将1.1直接赋值给float变量,因为这样属于向下转型(double精度大于float)。
float f = 1.1;//报错(检查异常)
float f = 1.1f;//正确
从java7开始,switch可以使用String类型作为判断条件, 一共支持char, byte, short, int, Character, Byte, Short, Integer, String, or an enum这几种类型。若类型过多,则使用if更加合适。
四、关键字
final
final可用于修饰类、方法、基本以及引用类型。
1、当用于基本类型,则该数值就无法改变;
2、若修饰引用类型,则该对象无法指向其他内存地址,但对象的属性都能修改;
3、若修饰的是方法,则方法无法被重写(private方法隐式地被指定为final,若子类中存在和基类一样的同名private方法,这个方法不是重写基类的方法,只是子类中新定义的一个方法,和基类无关);
4、若修饰的是类,则该类无法被继承。
static
static可以用于修饰内部类、方法、变量、代码块。
1、用于修饰变量,则会被整个类共享,可以通过类名直接访问,且只在内存中存有一份;
2、用于修饰方法,可以在类中通过方法名来调用,且必须有实现,不能是抽象方法;
3、用于修饰语句块,会在类初始化的时候执行一次;
4、修饰内部类,可以直接创建内部类实例,而不需要通过外部类来创建。
初始化的顺序: 1、首先是静态变量或者代码块,这两者之间的顺序由它们在代码中顺序决定(若存在父类和子类,则先父类再子类)。 2、其次是普通变量或者代码块,这两者之间的顺序同样由它们在代码中顺序决定(若存在父类和子类,则先父类再子类)。 3、最后是构造函数的初始化。
五、Object通用方法
public native int hashCode()
public boolean equals(Object obj)
protected native Object clone() throws CloneNotSupportedException
public String toString()
public final native Class<?> getClass()
protected void finalize() throws Throwable {}
public final native void notify()
public final native void notifyAll()
public final native void wait(long timeout) throws InterruptedException
public final void wait(long timeout, int nanos) throws InterruptedException
public final void wait() throws InterruptedException
equals()
用于比较两个对象。
1、对于基本类型,没有equals方法,只有==,==用于比较两个基本类型的值是否相等。 2、对于引用类型,==比较的两个对象的内存地址是否相同(即是否同一个对象),而equals则需要根据情况,就比如Object的equals实际和==是相同的,而Integer的equals的则是比较两个对象的值,equals可以根据需要进行重写。
hashcode()
hashcode方法用于计算hash值(也称为散列值),equals方法的重写一般伴随着hashcode的重写,因为需要保证等价的两个对象的hash值是相同的。但hash值相同不代表两个对象就等价,因为hash拥有随机性。
toString()
若当前类重写了,则按照重写的规则输出,否则,按照默认的ClassName(类名)@abcdefg这种形式,其中 @ 后面的数值为散列码的无符号十六进制表示。
clone()
克隆分为浅克隆和深克隆,深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的,对于基本数据类型是没有这种概念的。浅克隆较为常见,一个类需要去显式地去重写clone()后才能去调用该方法,并且需要实现cloneable接口。
对于浅克隆,它创建了一个新对象,如果克隆的对象是基本类型,则拷贝的是基本类型的值,如果是引用类型,则拷贝的是内存地址。
而对于深克隆,是创建了一个新的对象,并且拷贝了值,和被克隆的对象属于两个不同的对象,内存地址也不同。
使用clone去拷贝一个对象既复杂又存在风险,因此最好不要使用,可以通过拷贝构造函数来拷贝对象。拷贝构造函数就是一个构造函数的参数是一个当前类型的实例,然后将实例的各个属性赋值给新的实例。
六、继承
抽象类与接口
抽象类和抽象方法都是由abstract关键词来修饰的,如果一个类由抽象方法,那么这个类就必须是抽象类。抽象类是不能创建实例的,只能被继承。
接口中方法默认是public的,并且不允许被定义为private或protected。在jdk1.8中,接口可以有默认的实现,只需要使用default来修饰方法即可。接口中也可以创建字段,但都是默认static和final的。
子类可以使用super关键词来调用父类的构造函数、重写方法。
重写与重载
重写发生在不同的类中,而重载是在用一个类中。 重写一般出现在父类子类中,子类重写父类的方法,对方法的内容进行重写,以满足需要,重写的方法必须满足三个条件:
1、首先返回类型必须是父类的返回类型及其子类
2、修饰符的范围必须大于或等于父类
3、抛出的异常必须是父类抛出异常及其子类
如果使用了@override标签,编译器会自动检查是否满足上述条件。
重载就比如构造函数,同方法名,但参数类型、个数、顺序有任意一个以上不同,即认为是重载,但只是返回类型不同,则不算重载。
七、反射
每个类都有一个class对象,包含了和类相关的信息,在编译一个新创建的类文件时,会创建一个同名.class文件。 类的加载相当于class对象的加载,类在第一次使用时才会动态加载到jvm中,也可以通过Class.forName("com.mysql.jdbc.Driver")这种方式来控制类的加载,会返回一个Class对象。
我们可以通过全限定名来创建class对象,然后使用class对象通过反射获取到类的所有属性,通过invoke来调用类的方法,以及通过construct的newInstance来创建实例。
反射的缺点:
1、性能开销:反射涉及了动态类型的解析,所以jvm没办法对代码进行优化,在对性能要求高的地方应该避免使用。
2、安全限制:对于有安全要求的环境,就无法使用反射。
3、内部暴露:由于反射能做一些正常情况无法做到的事情(比如访问私有属性和方法等),所以可能导致意料之外的副作用,可能导致代码功能失调并破坏可移植性。
八、异常
异常中最大的类是Throwable,它分为error和exception。 Error是jvm无法处理的错误,常见的有OutOfMemory(内存溢出),StackOverFlow(堆栈溢出)。
而Exception是可分为运行时异常和非运行时异常。
运行时异常中常见的有IndexOutOfBounds(下标超出)、Arithmetic(算术异常)、illegalArgument(非法异常)、NullPointer(空指针)
非运行时异常有很多种,比如IOExcepiton、SqlException等, IOException有FileNotFound等
异常也可以根据是否受检查分为受检异常和非受检异常。
受检异常通常是指在代码为编译前就报出的错误以及极有可能出现的异常,比如float f = 1.1;需要在1.1后面加上f,又或者io流都需要放在try...catch中或用throws声明可能抛出的异常。
非受检异常就比如上面的运行时异常,是无法检查到的。
九、泛型
public class Box<T> {
// T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
十、特性
Jdk1.8特性
1、lambda表达式 2、管道、流 3、日期和时间API 4、默认方法
Jdk1.7特性
1、Switch判断条件能使用String 2、多重异常处理 3、nio