:::danger jvm中划分了5个区域:堆区、虚拟机栈、方法区、程序计数器、本地方法栈 :::
1.对象在内存中的存放形式
:::danger
JDK1.6及以前,常量池在方法区,这时的方法区也叫做永久代;
JDK1.8及以后,方法区又从堆内存中剥离出来了,但实现方式与之前的永久代不同,这时的方法区被叫做元空间,常量池就存储在元空间。
:::
2.对象创建过程
:::danger
- 首先在方法区中加载类信息(只会加载一次),具体步骤查看反射
- 然后在堆中开辟空间进行默认初始化(对static修饰的属性默认初始化)
- 对象初始化(调用构造器)
- 默认初始化,比如int设为0
- 显式初始化,如上图就是age=12
- 构造器初始化,根据构造器的内容初始化,字符串就先在方法区常量池中查找是否存在,不存在就创建新对象并放入常量池
- 把对象在堆中地址赋值给引用对象(上图中的cat)
当子类继承父类时,先从父类加载(此时static成员初始化),一直加载到子类,再从子类向上开始执行构造器,但是因为构造器中默认调用了super()与普通代码块,所以实际先是父类的普通初始化与构造器执行结束然后子类才开始普通初始化与构造器结束 :::
3.方法调用机制
4.继承的内存布局
new子类对象时,会把其父类也初始化,并且把父类的属性与方法放入子类对象的堆空间(包括私有,只是不能访问),且父子类同名对象不冲突(因为是属于不同的对象的),访问同名属性是就近原则,先访问子类,没有就父类,直到object,但是不会隔代访问,当父类的父类属性为Public,而父类同名属性为private,会访问失败(因为为子类没有时,是找父类存在的同名属性,但是父类中是存在的,只是不能访问,所以就不会再向上查找)。
5.多态
Object obj=new String(),等号左边叫编译类型,右边叫运行类型,编译类型不可变,编译时就已经确定
- instanceof 判断的是对象的运行类型是否为指定类的类型或者子类型
- 多态时,属性没有重写的说法,是看编译类型,而方法是看运行类型
- 向上转型不能调用子类特有的成员,可以调用父类所有成员,但是需遵守访问权限,最终运行效果看子类具体实现
- 动态绑定只是对方法而言的,属性没有动态绑定,哪里声明就在哪里使用,此处和上述第3点不冲突,此处因为子类的方法触发了动态绑定,在其内部使用i就是以当前i的声明所在类为准,而直接调用a.i是不走动态绑定的
6.finalize()
当对象没有任何引用是,jvm会使用垃圾回收器回收空间,并在回收前执行该方法,子类可以重新该方法
7.代码块
即类定义中只被{}包裹的代码,可以加static修饰成为静态代码块,代码块无需显式调用
- 静态代码块在类加载时被调用(和静态属性与静态方法一致),且只会被调用一次
- 普通代码块在每次对象被创建时调用
- 类在创建对象或者子类对象示例时、调用静态属性或方法时都会被加载
- 创建对象时,先执行静态初始化(包括静态属性、静态方法、静态代码块)、普通代码块初始化再执行构造器,同级别按照编写顺序执行
- 构造器中隐含了super()与普通代码块的调用
父类和子类之间的初始化顺序(原因看上方第2点中第5小点):
- 父类静态代码块与属性
- 子类静态代码块与属性
- 父类普通代码块与属性
- 父类构造器
- 子类普通代码块与属性
-
8.设计模式
1.单例模式
保证在程序运行中,一个类始终只会创建一个对象
将构造器私有化 (防止外部直接New)
- 在类的内部创建对象
- 向外暴露一个公共的getInstance()
1.饿汉式
在类加载时就自动创建好对象
2.懒汉式
在类加载时不预先创建对象,而是在getInstence()被调用时才创建
2.模板设计模式
抽象类的最佳实践,假如多个类需要实现差不多,但细节不同的功能,则可以把此方法公共部分提取到抽象类中,将变化的部分写成抽象方法,子类继承抽象类并实现抽象方法即可 ```java package review.design;
/**
模板设计模式 */ public class Template { public static void main(String[] args) {
//不同的运行类型调用的是自己对应的实现
BB bb = new BB();
bb.calculateTime();
CC cc = new CC();
cc.calculateTime();
} }
/**
抽象类作为模板 */ abstract class AA { //抽象方法交给子类实现不同的行为 public abstract void code();
public void calculateTime() {
long start = System.currentTimeMillis();
//此处动态绑定,调用具体子类的实现
code();
long end = System.currentTimeMillis();
System.out.println("执行耗时:" + (end - start));
} }
class BB extends AA { //继承并实现抽象方法 @Override public void code() { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } }
class CC extends AA { @Override public void code() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }
<a name="yZQDT"></a>
# 9.接口
1. 抽象类实现接口可以不用实现方法
1. jdk1.8之后接口中可以写默认方法和静态方法的实现,加**default**关键字标识
1. 接口中可以有属性,且只能是final,而且是**public static final**的
1. **接口不能继承类,但是可以继承接口**
1. 接口只能继承接口,不能实现接口且**接口只能被接口继承,可以多继承**
<a name="LgcjZ"></a>
# 10.内部类
定义在一个类内部的类,可以直接访问外部类的私有成员,且能够体现类之间的包含关系<br />四种内部类:
1. 定义在局部,例如方法上
1. 局部内部类(有类名)
1. **匿名内部类(无类名)**
1. 定义在成员上
1. 成员内部类
1. 静态内部类
<a name="e7Jam"></a>
## 1.局部内部类
1. 可以直接访问外部类所有属性,包括私有
1. **不能添加访问修饰符**,因为它的地位就相当于一个局部变量,但是可以使用final,因为局部变量也能用final修饰
1. **作用域**是定义所在的局部
1. 外部类访问局部类成员需要在局部类作用域内通过 对象.属性 来访问
1. 当外部类与内部类成员重名,遵循**就近原则**,如果要访问外部成员,使用 **外部类名.this.成员 **的形式
<a name="XLFyv"></a>
## 2.匿名内部类
1. 唯一一种**没有构造器**的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。
1. 本质上是一个重写或实现了父类或接口的**子类对象**
1. 匿名内部类在编译的时候由系统自动起名为Outter$1.class(外部类名+$1)。一般来说,匿名内部类用于继承其他类或是实现接口,**并不需要增加额外的方法,只是对继承方法的实现或是重写。**
1. **匿名内部类只能使用一次,再次实例化就不再是之前的名字了**(getClass)
<a name="Dd6hy"></a>
## 3.成员内部类
1. 地位和外部类成员相同,作用域相同
1. 外部类使用内部类,实例化之后调用
1. 外部类与内部类成员重名,同样遵守**就近原则**
1. **外部其他类使用内部类**:
1. 方式一,调用外部类对象的内部类构造器方法,看作为外部类的成员,创建一个类型为outer中的inner的变量,通过对象调用outer中的内部类构造器
```java
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
- 地位和外部类成员相同,作用域相同
- 不能直接访问非静态成员
外部其他类访问静态内部类,直接通过类名访问,就像访问静态成员那样
Outer.inner inner= new Outer.inner()
11.枚举类
:::danger 是一组常量的集合类 ,枚举类不能被克隆 :::
1.自定义类实现枚举
public class test11_16 { public static void main(String[] args) { System.out.println(enum1.ONE); } } enum enum1{ ONE(1), TWO(2); private Integer number;
enum1(Integer number) {
this.number = number;
}
}
<a name="wIBt9"></a>
# 12.注解
1. jdk5新增的功能,代码里的特殊标记,可以在编译、类加载、运行时被读取,并执行相关的操作,使得程序员可以在不改变原有逻辑下、嵌入一些新的功能,在javase中、注解的目的比较简单,比如标记过时的功能、忽略警告,**在javaee中占据了重要的角色例如用来配置程序的任何切面,代替javaee旧版的冗余的代码**
1. **自定义注解参照@SupperssWarnings定义**
1. **注解声明**使用**@interface**关键字替代class关键字
1. 内部定义成员通常使用value表示
1. 可以指定成员的默认值,使用**default**定义,如果自定义注解没有成员,表明是一个表示作用, 如果有成员,在使用时需要指明成员的值
1. ** 自定义注解必须配上注解的信息处理流(使用**[**反射**](https://www.yuque.com/rainbow-vl3cp/gqkckp/ai52tg)**获取注解信息后执行对应操作)才有意义,且@Retention必须为RUNTIME**
1. 自定义注解通常都会指明两个**元注解**:**Retention、Target**
3. jdk提供的**元注解**
1. 元注解:**对注解进行解释说明的注解**
1. **Retention:**指定所修饰的注解的生命周期**SOURCE**(编译时直接丢弃)/**CLASS**(记录在class文件中,但运行时,JVM不会保留,)默认行为 /**RUNTIME**(保留至运行时,可以通过反射获取到信息)
1. **Target**:用于指定所修饰的注解作用范围(能修饰哪些元素),成员值见ElementType类,例子在MyAnnotation注解类<br />********出现的频率较低*********
1. **Documentded**:被修饰的注解能够在被javadoc解析是保存到文档
1. **Inherited**:被修饰的注释具有继承性,即被他修饰的注释所修饰的类的子类也会被修饰
4. java8新特性
1. 可重复注解,即可以在一个声明上使用多次
1. 在myanaotation上修饰**@Repatable**,成员值为myanotations.class
1. myanaotation的元注解和myanaotations相同
2. 类型注解<br />ElementType增加常量:<br />ElementType.TYPE_PARAMETER : 注解能够卸载类型变量的声明语句中(泛型等)<br />ElementType.TYPE_USE : 能够写在任何语句中
```java
package annotationExample;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
//可重复注解
@Repeatable(MyAnnotations.class)
//作用范围,可选
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
//生命周期
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
//用方法形式,实际是变量
// 多个值
String[] value();
//一个值
String value1();
}
package annotationExample;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
MyAnnotation[] value();
}