基础概念
- 主函数:不是所有类都需要主函数,如果需要独立运行就需要定义主函数,主函数由jvm调用
- 成员变量和局部变量:
- 成员变量:定义在类中;成员变量在整个对象中有效;存在在堆中的对象中,生命期和对象相同;
- 局部变量:定义在函数中;局部变量在自己所属的大括号中有效;存在在栈中,生命期为从入栈到出栈;
- 一个类中多个构造函数,是重载的体现
- 类:在主类中new对象,执行顺序:静态代码块(加载类时执行,只执行一次)>main()>构造代码块>构造方法。
- 静态代码块:只在类加载执行一次
- 构造代码块:运行构造函数前执行,包含构造函数中的共性逻辑
- 构造函数:在与之对应的对象初始化时调用
class TestOrder{
//可以同时有多个静态代码块
static {
System.out.println("static block.");
}
static {
System.out.println("static block2");
}
// 可以同时有多个构造代码块
{
System.out.println("construct block2.");
}
TestOrder(){
System.out.println("construct function");
}
{
System.out.println("construct block.");
}
private void printMethod(){
System.out.println("print");
}
public static void main(String[] args){
// 执行顺序:静态代码块-》main-》构造代码块-》构造函数
System.out.println("main function");
new TestOrder().printMethod();
/*
* static block.
static block2
main function
construct block2.
construct block.
construct function
print
* */
}
}
- Person p = new Person();初始化过程
- 加载Person.class
- 在栈中分配p
- 在堆中分配一个空间
- 在分配的空间中为属性分配空间,然后对属性默认初始化
- 对属性显示初始化
- 构造代码块初始化
- 构造函数初始化
- 将堆中的对象的首地址赋值给p
- 对象新建的方法:new、反射、clone、序列化(ObjectInputStream、ObjectOutputStream)
封装
- 好处:将变化隔离;便于使用;提高重用性;安全性
- this:代表所在函数所属对象。哪个对象调用这个函数,this就是哪个对象
- 用this调用构造函数,必须定义在构造函数的第一行
static
- 对于静态变量在内存中只有一份拷贝,在方法区中,所有实例会共享这个静态变量。
- 对于非静态变量每创建一个实例就会在堆中分配一个,存在多个拷贝
- 静态方法中不能使用this,不能访问实例变量。
- 因为this和具体的实例关联,静态方法没法确定要和哪个实例关联。
- 静态方法优先于对象存在,所以没有办法访问对象中的成员
- 静态代码块,可以存在多个,在类加载时按先后顺序依次执行它们。只执行一次
- static final:可以理解为全局常量,一旦赋值不能修改,直接通过类名引用
- 静态代码块如果和主函数在同一类中,优先于主函数执行
final
- 代表最终态,不能被改变
- final类不能有子类,其中的方法默认是final。
- 主要用在类不能被修改扩展时
- final方法不能被子类重写,但可以被继承。
- 主要是为了把方法锁定不允许子类修改;
- 编译器在遇到final会变成内嵌提高处理效率
- final变量表赋值一次后不能修改。
- 主要有静态变量、实例变量和局部变量。一旦给final变量初值后,值就不能再改变了
- final不能修饰构造函数
- 函数参数为final时,将只能读取不能修改
- static final:
- 可能会有风险:如果这个常量依赖第三方库中的编译时常量,一旦这个常量变化了,但是你的jar仍然是旧值,所以更新依赖后需要及时重新编译
常量的风险:https://blog.csdn.net/Honeyhanyu/article/details/77878120?locationNum=6&fps=1
- public static final int a = 10就是一个编译时常量,编译后将直接替换成10,a将不存在
- public static final int b = “hello”.length()就是一个运行时常量
- final类不能有子类,其中的方法默认是final。
- final防止重排序
- 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
Person p=new Person();
- 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。
Person p=obj;System.out.println(obj.finalAttr);
- 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
https://blog.csdn.net/qq1028951741/article/details/53418852 final防止重排序
继承
- 类,对父类单继承、接口多实现
- 父类子类中出现相同名称的成员变量时,可以使用this和super进行区分
- 重写:子类和父类出现相同方法时(方法名和参数相同,如果只是返回值不同编译将报错),子类对象调用方法为子类上的方法。
- 子类中的访问权限要大于等于父类。(如果小于父类,在多态的情况下将无法访问)
- 实例方法非静态方法只能重写非静态方法(编译直接报错)
- 静态方法不能被重写,只能被子类隐藏
- 构造函数:子类中构造函数会先调用父类的构造函数
- super()和this()都需要定义在第一行,所以构造函数中只能二选一
- 有时继承会破坏封装性,这个时候可以使用final来处理,将不想被重写的方法设为final
abstract
- 抽象方法必须定义在抽象类中,要实现了所有的抽象方法才能实例化
- 抽象类中可以定义抽象方法,由子类来进行实现
- 抽象类中是可以有非抽象方法的
- abstract不可以与以下修饰符共存
- final:修饰后子类无法实现
- private:修饰后子类没有访问权限
- static:static修饰的方法不用实例化直接调用,但是abstract没有实现
- 抽象类中可以没有抽象方法,但是这样没有意义,只是让该类不能实例化
interface
- 包含全局常量(public static final)、抽象方法(public abstract)
- interface中可以不用写public
- 接口与接口之间存在着继承关系,接口可以多继承接口
interface A{} interface B{} interface C extends A,B{}
- 没有多继承主要是为了避免“菱形问题”,但是现在java8中引入了接口的默认方法,有可能出现菱形问题
- 接口主要是用于扩展功能;抽象类是为了将公共方法进行抽取出来,特有方法交给子类实现
多态
- 父类引用或者接口的引用指向了自己子类或实现类的对象。
- 好处:提高扩展性。子类只要重写父类的方法,父类引用指向子类实例,父类引用就可以直接调用子类重写的方法
- 弊端:父类引用只能访问父类中有的方法,不能访问子类的特有方法
- 如果想用子类特有方法,用instanceof判断类型后,对对象进行强转
- 前提:有继承和重写;或者存在接口实现
- 在虚拟机层面是通过动态绑定来实现多态
Object
- 所有类的父类
boolean equals(Object obj)//比较两个对象的地址
String toString()//将对象变成字符串
Class getClass()//获取对象的类类型
int hashCode()//将该对象的内部地址转换成一个整数来实现
finalize() //对象在回收前调用,这是最后可以获取到对象的时候
clone()// 浅拷贝:所有变量的值和之前相同,但是引用变量依然指向以前的对象
//深拷贝:所有变量的值和之前相同,引用变量将指向被拷贝的新对象
内部类
- 如果A类需要直接访问B类中的成员,而B类又需要建立A类的对象;将A类定义在B类中
- 内部类:可以直接访问外部类的属性
- 外部类:想要访问内部类(非静态内部类),需要先实例化内部类
- 定义在成员变量位置
- 默认修饰符时访问内部类:
Outer.Inner in = new Outer.new Inner()
一般很少这样用,都是通过父类方法来获取:Set<Map.Entry<String,String>> entrySet =map.entrySet();
- private的内部类:外部无法访问
- static的内部类:外部可以直接访问,
- 内部类中定义static成员,那么该内部类必须是静态的
- 默认修饰符时访问内部类:
- 定义在局部变量位置
- 当内部类被定义在局部位置上,只能访问局部中被final修饰的局部变量。
- 匿名内部类:没有名字的内部类。如果一个子类后只使用一次,或者作为参数传给方法,可以使用匿名内部类
- 定义匿名内部类:需要前提,内部类必须继承一个类或者实现接口。
final int a=10;
new Thread(new Runnable() {
@Override
//定义了一个匿名内部类
public void run() {
System.out.println(a);
}
}).start();
- 下面两个都是通过匿名内部类建立一个Object类的子类对象
//1
new Object(){
void show(){
System.out.println("show run");
}
}.show(); //写法和编译都没问题
//2
Object obj = new Object(){
void show(){
System.out.println("show run");
}
};
obj.show(); //写法正确,编译会报错
//匿名内部类是Object的一个子类,这里赋给Object,造成多态,所以obj只能访问Object中定义过的方法
异常
- java.lang.Throwable
- Error:通常是jvm发生 (eg:OutOfMemoryError)
- Exception:异常(eg:IOException)
- RuntimeException:运行时异常(eg:NullPointException)
- throw:抛出的一个具体的异常类型;throw用在函数内
- throws:声明一个方法可能抛出的所有异常信息;throws用在函数上,可能抛出多个异常
- 异常处理:
- 自己捕获异常:try……catch
- 抛出异常:throw new Exception()
- finally:注意不要在finally中return或者抛出异常
- 异常在继承中:
- 父类没有异常声明抛出,子类也不能有异常抛出。如果真的需要抛异常,也只能抛RuntimeException异常
- 可以想象在多态的情况下,父类上没有声明异常,子类却声明抛出异常。使用者将无法确定是否需要捕获
不可变对象
- java中的Integer、String都是不可变对象,一旦修改将生成新对象
自定义不可变对象
- 将变量改为private和final
- 提供一个带参数的构造函数
- 不提供setter
- 如果其中的成员变量有引用类型:在初始化时重新new一个切断和创建者的关系;get()时重新new一个实例保证外部修改不会影响到自己。