Java高级

Posted by Wh0ami-hy on February 12, 2023

1. Annotation 注解

可以被其他程序(比如:编译器等)读取

注解是以 @注解名 在代码中存在的,还可以添加一些参数值

@SuppressWarnings(value="unchecked")

可以附加在package、class、method、 field 等上面,相当于给他们添加了额外的辅助信息

可以通过反射机制编程实现对这些元数据的访问

1.1. 内置注解

@Override

@Override注解,只能用于标记方法,并且它只在编译期生效,不会保留在class文件中

@Override注解用于指示一个方法覆盖了其父类中的方法

@Deprecated

@Deprecated用于标记一个类、方法或字段已经被弃用, 不应该再被使用

@SuppressWarnings

@SuppressWarnings 注解是用来抑制编译器发出的特定警告的一种方式

1.2. 元注解

负责注解其他注解

@Target

表示我们的注解可以用在哪些地方

@Retention

表示我们的注解在什么地方有效,runtime>class>sources

@Documented

表示是否将我们的注解生成在JAVAdoc中

@Inherited

说明子类可以继承父类中的该注解

1.3. 自定义注解

@interface用来声明一个注解

规定注解的参数格式:参数类型 参数名()

参数只能是基本类型 Class、String 、enum等

可以通过 default 来声明参数的默认值,注解元素必须要有值,我们定义注解元素时,经常使用空字符串或0作为默认值

// 使用自定义注解
// 注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
@MyAnnotation(name = "嘿嘿")
public class annotate {
    public static void main(String[] args) {
        System.out.println("asd");
    }
}
// 自定义注解
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ interface MyAnnotation{
    // 规定注解的参数:参数类型 + 参数名 + ()
    String name();
    // 通过 default 来声明参数的默认值
    int age() default 0;
}

2. Reflection 反射

反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法

正常方式

graph LR
A[引入需要的'包类'名称]
A-->B[通过new实例化]
B-->C[取得实例化对象]

反射方式

graph LR
A[实例化对象]
A-->B[getClass 方法]
B-->C[得到完整的'包类'名称]

2.1. 何为反射

加载完类之后,在堆内存的方法区中会产生一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。可以通过这个对象看到类的结构。这个对象反射出了完整的类的结构信息,所以称为反射

反射对性能有影响

2.2. Reflection API

反射相关的主要API

Class 代表一个类

Method 代表类的方法

Field 代表类的成员变量

Constructor 代表类的构造器

3. Class类

一个类被加载后,class对象就会产生,并且类的整个结构都会被封装在Class对象中

在Object类中定义了以下的方法,此方法将被所有子类继承

// 用于获取对象的运行时对象的类
public final Class getClass()

3.1. 特点

Class本身也是一个类
Class对象只能由系统建立,在堆中存放
一个加载的类在JVM中只会有一个Class实例
一个Class对象对应的是一个加载到JVM中的一个.class文件
每个类的实例都会记得自己是由哪个Class实例所生成
通过Class可以完整地得到一个类中的所有被加载的结构
Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

3.2. 获取类的运行时结构

方法 功能
static ClassforName(String name) 返回指定类名的Class对象
Object newInstance() 调用缺省(即默认)构造函数,返回Class对象的一个实例
getName() 返回此Class对象所表示的实体(类,接口,数组类或void)的名称
Class getSuperClass() 返回当前Class对象的父类的Class对象
Class[] getinterfaces() 获取当前Class对象的接口
ClassLoader getClassLoader() 返回该类的类加载器
Constructor[] getConstructors() 返回一个包含某些Constructor对象的数组
Method getMothed(String name,Class.. T) 返回一个Method对象,此对象的形参类型为param Type
Field[] getDeclaredFields() 返回类的所有属性

3.3. 获取Class类的实例

若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高

Class cla = 类名.class

已知某个类的实例,调用该实例的getClass()方法获取Class对象

Class cla = 对象名.getClass()

已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException

Class cla = Class.forName("com.baidu.www.类名");

基本数据类型的包装类可以直接用类名.Type

Integer.TYPE

还可以利用ClassLoader

3.4. 哪些类型有Class对象

class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
interface:接口
[]:数组
enum:枚举
annotation:注解@interface
primitive type:基本数据类型
void

4. 类加载内存分析

graph LR
A(Java内存)
A-->A.1(堆)
A-->A.2(栈)
A-->A.3(方法区)
A.1-->A.1.1(存放new的对象和数组)
A.1-->A.1.2(可以被所有的线程共享.不会存放别的对象引用)
A.2-->A.2.1(存放基本变量类型,会包含这个基本类型的具体数值)
A.2-->A.2.2(引用对象的变量,会存放这个引用在堆里面的具体地址)
A.3-->A.3.1(可以被所有的线程共享)
A.3-->A.3.2(包含了所有的class和static变量)

加载

一个类被加载后,class对象就会产生

链接

链接的准备阶段,为static分配内存

初始化

执行类构造器<clinit> ()方法

4.1. 分析类初始化

类的主动引用

一定会发生类的初始化

当虚拟机启动,先初始化main方法所在的类

new一个类的对象

调用类的静态成员(除了final常量)和静态方法

使用java.lang.reflect包的方法对类进行反射调用

当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类

public class demo {
    static {
        System.out.println("Main类被加载");
    }
    public static void main(String[] args) throws Exception {
        Son s1 = new Son();
    }
}
class Father{
    static int b = 2;
    static {
        System.out.println("父类被加载");
    }
}
class Son extends Father{
    static {
        System.out.println("子类被加载");
    }
    static int m = 100;
    static final int M = 1;
}

类的被动引用

不会发生类的初始化

当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化

通过数组定义类引用,不会触发此类的初始化

引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)

4.2. 类加载器

类加载器作用是用来把类(class)装载进内存的

5. 动态创建对象

5.1. 创建类的对象

调用Class对象的newInstance()方法

类必须有一个无参数的构造器
类的构造器的访问权限需要足够

调用类中的构造器,传递参数,进行实例化

通过Class类的getDeclaredConstructor(Class .. parameterTypes)取得本类的指定形参类型的构造器
向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数
通过Constructor实例化对象

5.2. 调用类的方法

通过Class类的getMethod(String name,Class… parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型

之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息

使用 Object invoke(Object obj, Object … args) 激活

若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象setAccessible(true)方法,取消Java语言访问检查

6. 获取泛型信息

7. 获取注解信息

Annotation[] annotations = c1.getAnnotations()


本站总访问量