【Java笔记】笔记记录(一)
Java基础知识复习
一:不可变类
定义:一个类的对象被创建出来以后,它的值便不能被修改了,类似于常量。
常见问题:String类是一个不可变类,那么为什么可以对String对象进行操作呢?
表面上看String对象值是修改了,其实不是,String s = "Hello"声明了一个指向String类型对象的引用,这个引用的名字为s,它指向了一个字符串常量"Hello"。s= s+"World"并没有改变所指向的对象,这句代码运行后,s指向了另一个String类型的对象,该对象的内容为"HelloWorld",原来那个对象还在内存中,没有被改变。
另外:String str = new String("Hello")会发生什么呢?
总之,new出来的对象指向的是内存中的地址而不是常量池中的地址。
如何创建一个不可变类呢?
1)类中的所有成员变量被private修饰
2)类中没有改写成员变量的方法
3)确保类中的方法不会被之类覆盖,可以通过final实现
4)如果成员变量是引用类型,要确保这个变量的值不发生变化,可以通过clone方法将对象复制
引申:为什么存储密码时用数组比用String类型安全?
因为String是不可变类,它存储在字符串常量池中,就算这个字符串不再被使用,它还是会在常量池中存在一段时间,此时,有权限访问常量池的程序都可能访问到这个字符串,从而造成泄露。
二:值传递和引用传递
对于StringBuild和StringBuffer来说,如果new了一个对象,并创建一个方法将这个对象传入并使用append方法在对象后面加值,你会发现对象的值发生了变化,从而判断这二者是引用传递,因为在方法体中改变了原来的值,但是你如果在方法体中将这个对象重新new一个值,你会发现之前的对象的值没有发生变化,从这个角度上看,它们的本质还是值传递,其实,在第一种传递时,这个方法是创建了一个新的对象,并将形参对象指向这个新的对象,由于指向同一个内存空间,原来的实参对象的指向也发生了变化。
而在第二种方式中,只是形参的指向发生了变化,所以原来实参并不会变化。
三:Java关键字
static:
作用:为某特定的数据类型或对象分配单一的存储空间,而与创建的对象个数无关,并且实现某个方法或属性与类而不是对象关联在一起,于是可以不创建对象直接调用该方法。
也就是说被static修饰的代码块只会运行一次。
可以修饰的元素:
变量:静态变量,可以跨越代码块访问
方法:静态方法,可以跨越代码块进行访问
代码块:静态代码块,只能定义在类定义下,在类被加载时执行
导入包:静态导入包,导入指定的static变量,import static xxx.*;导入该包下的所有static变量
final:
作用:用于声明属性、方法和类,分别表示属性不可变,方法不可覆盖,类不可被继承。
注意:final指的不可变时引用不可变,而不关心实际对象的内容变化。
问题:为什么匿名内部类只能使用成员变量或者被final修饰的局部变量?
因为匿名内部类的代码可能会延迟执行,在外部的方法体的变量都消亡时,只有被final修饰的变量会在匿名内部类中有一个引用的副本。
transient:
作用:当持久化一个对象的时候,可能并不想持久化这个对象的所有属性,这时使用transient修饰的属性不会被持久化。
引申:如何实现序列化?
实现Serializable接口,它里面没有任何方法,你需要使用一个输出流来构造一个对象流对象,使用该对象流的writeObject方法将对象写出,同理恢复这个对象时要使用输入流。
序列化的特点:
1、如果一个对象能被序列化,那么他们的之类也能够被序列号
2、声明为static和transient不能被序列化,因为transient代表对象的临时数据,static代表类成员
3、子类实现了Serializable接口父类没有,子类可以被序列化,父类不行。
volatile:
作用:该字段用于修饰会被多线程访问的属性,以保持该属性的修改对所有线程可见。
相比较与synchronized,它仅用于修饰字段,并且volatile只能保持线程安全三要素中的可见性和有序性,不能保证操作的原子性。
可见性:对于共享变量,一个线程对共享变量的修改,其它线程能够立刻看到。
有序性:代码按照顺序执行
原子性:要么全部执行,要么全部不执行
实现原理:
当一个volatile字段被修改,JVM会立刻执行一个Write-Barrier操作,将当前处理器缓存的数据写会系统内存,并且使其它cpu核心里引用了该地址的数据变成脏数据。
当读取时,JVM会多执行一个Read-Barrier指令,如果该数据已经变脏,那么从主存中重新获取数据。
四:多重继承
Java本身并不支持多重继承,但是可以通过其他方法间接实现多重继承,具体有以下两个方法:
1)通过接口实现
class Duck extends Animal implements CanFly,CanRun{
}
2)通过内部类实现
在一个类中定义一些内部类,让这些内部类继承不同的父类。
class A{
class C extends B{}
class D extends E{}
}
五:反射
反射主要实现了以下功能:
1)获取类的访问能修饰符、方法、属性以及父类信息。
2)在运行时根据类的名字创建对象。在运行时调用任意一个对象的方法
3)在运行时判断一个对象属于哪个类
4)生成动态代理
重要的类:
Class类:
获取方法:
xxx.Class; 不执行静态代码块和动态构造块。
Class.forName("xxx");只执行静态代码块。
new xxx().getClass()执行静态代码块和动态构造块。
此类里的方法主要有三类:
获取类的构造方法
getConstructors()返回全部public构造方法
getConstructor(Class<?>... parameterTypes)返回指定的public构造方法
getDeclareConstructors()返回全部构造方法
getDeclareConstructor(Class<?>... parameterTypes)返回指定的构造方法
获取类的成员变量
getFields();获得全部public成员变量
getField(String name);获得指定public成员变量
getDeclareFields();获得全部成员变量
getDeclareField(String name);获得指定车关于变量
获取类的方法
getMethods();获得public方法
getMethod(String name,Class<?>... parameterTypes);获得指定public方法
getDeclareMethods();获得全部方法
getDeclareMethod(String name,Class<?>... parameterTypes);获得指定方法
六:嵌套类
内部类分为四种:
静态内部类:不依赖外部类实例而被实例化,静态内部类不能访问外部类的普通成员变量,只能访问静态成员和静态方法
class A{
static class inner{}
}
成员内部类
class A{
class inner{}
}
局部内部类
class A{
public void method(){
class inner{}
}
}
匿名内部类:没有类名,没有构造函数,但是必须继承其他类或实现其它接口,好处时更加简洁、紧凑,但带来的问题是易读性下降。
匿名内部类不能有构造函数
不能定义静态成员、方法、类
不能是public、protected、private、static
只能创建匿名内部类的一个实例
一个匿名内部类一定是在new后面
public class MyFrame extends Frame{
public MyFrame(){
addWindowListener(new WindowAdapter(){
});
}
}