java基础知识
1. Java中的基本数据类型
基本类型 | 大小(字节) | 默认值 | 封装类 |
---|---|---|---|
byte | 1 | (byte)0 | Byte |
short | 2 | (short)0 | Short |
int | 4 | 0 | Integer |
long | 8 | 0l | Long |
float | 4 | 0.0f | Float |
double | 8 | 0.0d | Double |
boolean | 4 | false | Boolean |
char | 2 | \u0000(null) | Character |
注意:在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位。这样我们可以得出boolean类型占了单独使用是4个字节,在数组中又是1个字节。使用int的原因是,对于当下32位的处理器(CPU)来说,一次处理数是32位(这里不是指的是32/64位系统,而是指CPU硬件层面),具有高效存取的特点。
2. 重载和重写的区别
重写(Override)
从字面上看,重写就是 重新写一遍的意思。其实就是在子类中把父类本身有的方法重新写一遍。子类继承了父类原有的方法,但有时子类并不想原封不动的继承父类中的某个方法,所以在方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法返回值的子类时)都相同的情况下, 对方法体进行修改或重写,这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。
public class Father {
public static void main(String[] args) {
// TODO Auto-generated method stub
Son s = new Son();
s.sayHello();
}
public void sayHello() {
System.out.println("Hello");
}
}
class Son extends Father{
@Override
public void sayHello() {
// TODO Auto-generated method stub
System.out.println("hello by ");
}
}
重写 总结:
- 发生在父类与子类之间
- 方法名,参数列表,返回类型(除过子类中方法的返回类型是父类中返回类型的子类)必须相同
- 访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private)
- 重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常
重载(Overload)
在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同甚至是参数顺序不同)则视为重载。同时,重载对返回类型没有要求,可以相同也可以不同,但不能通过返回类型是否相同来判断重载。
public class Father {
public static void main(String[] args) {
// TODO Auto-generated method stub
Father s = new Father();
s.sayHello();
s.sayHello("wintershii");
}
public void sayHello() {
System.out.println("Hello");
}
public void sayHello(String name) {
System.out.println("Hello" + " " + name);
}
}
重载 总结:
- 重载Overload是一个类中多态性的一种表现
- 重载要求同名方法的参数列表不同(参数类型,参数个数甚至是参数顺序)
- 重载的时候,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准
3. 对内部类的理解
主要目的是提高安全性。将一个类定义在另一个类里面或者一个方法里面
成员内部类:在外部类的内部定义一个内部类。
局部内部类:在方法中定义内部类;不能用public和private修饰;所在方法外的代码访问/调用不到该类;可以访问被声明为final的局部变量;
静态内部类: 被static修饰的成员内部类,属于外部类本身,不属于外部类某个对象;只能访问外部类静态成员;可以用public、protected、private修饰;
匿名内部类:一定跟在new后面,创立其实现的接口或父类对象;不能有构造方法;不能定义任何静态成员、方法、类,可定义非静态的方法、属性、内部类;只能创建一个实例;
4.Java中的四种引用
强引用
强引用是平常中使用最多的引用,强引用在程序内存不足(OOM)的时候也不会被回收,使用
方式:
String str = new String("str");
System.out.println(str);
软引用
软引用在程序内存不足时,会被回收,使用方式:
// 注意:wrf这个引用也是强引用,它是指向SoftReference这个对象的,
// 这里的软引用指的是指向new String("str")的引用,也就是SoftReference类中T
SoftReference<String> wrf = new SoftReference<String>(new String("str"));
可用场景: 创建缓存的时候,创建的对象放进缓存中,当内存不足时,JVM就会回收早先创建的对象。
弱引用
弱引用就是只要JVM垃圾回收器发现了它,就会将之回收,使用方式:
WeakReference<String> wrf = new WeakReference<String>(str);
可用场景: Java源码中的 java.util.WeakHashMap 中的 key 就是使用弱引用,我的理解就是,一旦我不需要某个引用,JVM会自动帮我处理它,这样我就不需要做其它操作。
虚引用
虚引用的回收机制跟弱引用差不多,但是它被回收之前,会被放入 ReferenceQueue 中。注意哦,其它引用是被JVM回收后才被传入 ReferenceQueue 中的。由于这个机制,所以虚引用大多被用于引用销毁前的处理工作。还有就是,虚引用创建的时候,必须带有 ReferenceQueue ,使用例子:
PhantomReference<String> prf = new PhantomReference<String>(new String("str"),
new ReferenceQueue<>());
可用场景: 对象销毁前的一些操作,比如说资源释放等。 Object.finalize() 虽然也可以做这类动作,但是这个方式即不安全又低效
上诉所说的几类引用,都是指对象本身的引用,而不是指Reference的四个子类的引用(SoftReference等)。
5.HashCode的作用
java的集合有两类,一类是List,还有一类是Set。前者有序可重复,后者无序不重复。当我们在set中插入的时候怎么判断是否已经存在该元素呢,可以通过equals方法。但是如果元素太多,用这样的方法就会比较满。于是有人发明了哈希算法来提高集合中查找元素的效率。 这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储的那个区域。hashCode方法可以这样理解:它返回的就是根据对象的内存地址换算出的一个值。这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。
6.有没有可能两个不相等的对象有相同的hashcode
可能.在产生hash冲突时,两个不相等的对象就会有相同的 hashcode 值.当hash冲突产生时,一般
有以下几种方式来处理:
- 拉链法:每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链
表,被分配到同一个索引上的多个节点可以用这个单向链表进行存储. - 开放定址法:一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总
能找到,并将记录存入 - 再哈希:又叫双哈希法,有多个不同的Hash函数.当发生冲突时,使用第二个,第三个….等哈希函数
计算地址,直到无冲突
7.深拷贝和浅拷贝的区别
原型模式:设计模式 --> Spring bean的Scope
浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指
向原来的对象.换言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象.
深拷贝:被复制对象的所有变量都含有与原来的对象相同的值.而那些引用其他对象的变量将指向
被复制过的新对象.而不再是原有的那些被引用的对象.换言之.深拷贝把要复制的对象所引用的
对象都复制了一遍.
8.Object中的常用方法
clone 方法
保护方法,实现对象的浅复制,只有实现了 Cloneable 接口才可以调用该方法,否则抛出
CloneNotSupportedException 异常,深拷贝也需要实现 Cloneable,同时其成员变量为引用类型
的也需要实现 Cloneable,然后重写 clone 方法。
finalize 方法
该方法和垃圾收集器有关系,判断一个对象是否可以被回收的最后一步就是判断是否重写了此方
法。
equals 方法
该方法使用频率非常高。一般 equals 和 == 是不一样的,但是在 Object 中两者是一样的。子类一
般都要重写这个方法。
hashCode 方法
该方法用于哈希查找,重写了 equals 方法一般都要重写 hashCode 方法,这个方法在一些具有哈
希功能的 Collection 中用到。
一般必须满足 obj1.equals(obj2)==true 。可以推出 obj1.hashCode()==obj2.hashCode() ,但是
hashCode 相等不一定就满足 equals。不过为了提高效率,应该尽量使上面两个条件接近等价。
- JDK 1.6、1.7 默认是返回随机数;
- JDK 1.8 默认是通过和当前线程有关的一个随机数 + 三个确定值,运用 Marsaglia’s xorshift
scheme 随机数算法得到的一个随机数。
wait 方法
配合 synchronized 使用,wait 方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait() 方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。
调用该方法后当前线程进入睡眠状态,直到以下事件发生。
- 其他线程调用了该对象的 notify 方法;
- 其他线程调用了该对象的 notifyAll 方法;
- 其他线程调用了 interrupt 中断该线程;
- 时间间隔到了。
此时该线程就可以被调度了,如果是被中断的话就抛出一个 InterruptedException 异常。
notify 方法
配合 synchronized 使用,该方法唤醒在该对象上等待队列中的某个线程(同步队列中的线程是给抢占 CPU 的线程,等待队列中的线程指的是等待唤醒的线程)。
notifyAll 方法
配合 synchronized 使用,该方法唤醒在该对象上等待队列中的所有线程。
总结
只要把上面几个方法熟悉就可以了,toString 和 getClass 方法可以不用去讨论它们。该题目考察的是对 Object 的熟悉程度,平时用的很多方法并没看其定义但是也在用,比如说:wait() 方法,equals() 方法等。
Class Object is the root of the class hierarchy.Every class has Object as a
superclass. All objects, including arrays, implement the methods of this class.
大致意思:Object 是所有类的根,是所有类的父类,所有对象包括数组都实现了 Object 的方法。
9.Java 创建对象的方式
new 关键字
平时使用的最多的创建对象方式
User user=new User();
反射方式
使用 newInstance(),但是得处理两个异常 InstantiationException、IllegalAccessException:
User user=User.class.newInstance();
Object object=(Object)Class.forName("java.lang.Object").newInstance()
clone方法
Object对象中的clone方法来完成这个操作
反序列化操作
调用 ObjectInputStream 类的 readObject() 方法。我们反序列化一个对象,JVM 会给我们创建一个单独的对象。JVM 创建对象并不会调用任何构造函数。一个对象实现了 Serializable 接口,就可以把对象写入到文中,并通过读取文件来创建对象。
总结
创建对象的方式关键字:new、反射、clone 拷贝、反序列化。
10.对Java集合的理解
下面是集合框架的类图结构
注意:
TreeSet的本质是值为null的TreeMap
HashSet的本质是值为null的HashMap
11.对红黑树的理解
红黑色的本质:2-3-4树
红黑树保证黑节点平衡的方式:左旋/右旋+变色 来保证
12.try-finally中的return关键字
return语句的本质:
- return语句获取到变量的地址
- return将获取的地址返回,也就是return本质是传地址
测试案例代码:
public class Demo02 {
public static void main(String[] args) {
Too too=new Too();
StringBuilder t1=test(too);
System.out.println("return语句返回的:"+t1+"\t返回值的hashCode:"+t1.hashCode());
System.out.println("finaly里面修改的:"+too.num+"\tfinaly的hashCode:"+too.num.hashCode());
}
public static StringBuilder test(Too too) {
try {
too.num=new StringBuilder("try");
System.out.println("try字符串的hashcode:"+("try").hashCode());
System.out.println("StringBuilder里的try的hashCode:"+too.num.hashCode());//--语句1
return too.num; //语句2
} finally {
too.num=new StringBuilder("finaly");//语句3
System.out.println("finaly的hashCode:"+too.num.hashCode());//语句4
}}}
class Too{
StringBuilder num=new StringBuilder("你好");
}
输出结果:
try字符串的hashcode:115131
StringBuilder里的try的hashCode:460141958
finaly的hashCode:1163157884
return语句返回的:try 返回值的hashCode:460141958
finaly里面修改的:finaly finaly的hashCode:1163157884