1. 注解
定义
注释 commont 给人看的
注解 annotation 给程序解释 可以被起程序读取
注解的作用:通过注解标识,程序解析时,解析到注解,会去找哪块引用了注解,根据注解功能的不同对业务进行扩展
格式
以"@注解名"在程序中存在的,还可以添加一些参数值 如:
@EnableAspectJAutoProxy(proxyTargetClass = true)
位置
可以附加在 package , class , method , field 等 上面,相当于给他们添加了额外的辅助信息 , 我们可以通过反射机制实现对注解的访问
1.1 java 内置注解
@Override
定义在java.lang.Override中,只适用于修饰方法,表示一个方法重写超类中的某个方法
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@Deprecated
定义在java.lang.Deprecated中,表示不鼓励程序员使用这样的元素,当时可以使用,通常存在更好的选择
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
@SuppressWarnings
镇压警告,抑制编译时的警告信息(标黄的元素),通常是定义后未被使用的元素
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
}
1.2 java元注解
- 元注解负责解释其他注解,java定义了4个标准的meta-annontation类型,他们用来提供对其他注解类型做说明
@Target
描述注解的使用范围,即被标识的注解可以用在什么地方
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
ElementType[]
是一个枚举类型,可以传递一个数字,标识注解作用域,即在哪个地方有效
举例:
@Retention
标识注解的生命周期
SOURCE < CLASS < RUNTIME
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
@Documented
表示是否将注解生成在Javadoc中
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
@Inherited
说明子类可以继承父类中的注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
1.3 自定义注解
定义规则
案例
package com.jt.anno;
/**
* 自定义注解
* 注解有多个参数时,使用时不需要关注参数顺序
* 注解只有一个参数且参数名为value,参数赋值时可省略value
*/
@MyAnnotation(name = "吵吵")
public class Test01{
}
@MyAnnotation02("猪八戒")
class Test02{
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
/**
* 注解的参数不是一个方法,有自己的格式
* 参数类型 + 参数名();
* 定义了参数,使用注解时就必须赋值
* 或者可以加一个默认值 default "" 为空即可
*/
String name() default "";
int age() default 0;
int id() default -1; //默认值为-1代表不存在
String[] school() default {"清华","北大"};
}
/**
*如果注解只有一个参数,建议使用value命名
* 使用注解时,为参数赋值可省略value
* @author Cmj
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation02{
String value() default "";
}
2. 反射机制
Reflection 赋予java语言动态性,创建对象(除 new 和 clone )的另一种形式,可以获取类的注解,泛型等
2.1 反射机制概述
动态语言和静态语言
- 动态语言
是一类在运行时可以改变其结构的语言,在运行时,代码可以根据某些条件改变自身结构 如: JavaScript,PHP,Python等 - 静态语言
运行时结构不可改变,如Java , C, C++等,java准确的说是类动态语言,我们可以通过反射机制获得类似动态语言的特性
- 一个类在内存中只有一个class对象
- 一个类在被加载后, 类的整个结构都会被封装在class对象中
package com.jt.reflection;
/**
* @author CMJ
* 2021/6/14 17:14
*/
public class TestReflection {
/*** 手写的类路径JVM有可能找不到,所以会有ClassNotFoundException异常信息*/
public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取类的Class对象,类路径是人写的,所以不确定泛型约束类型是什么
Class<?> c1 = Class.forName("com.jt.pojo.User");
System.out.println(c1);
//结果: class com.jt.pojo.User class类型的user对象
Class<?> c2 = Class.forName("com.jt.pojo.User");
Class<?> c3 = Class.forName("com.jt.pojo.User");
Class<?> c4 = Class.forName("com.jt.pojo.User");
/**
* 一个类在内存中只有一个class对象
* 一个类在被加载后, 类的整个结构都会被封装在class对象中
*/
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
System.out.println(c4.hashCode());
}
}
2.2 JVM如何加载类获取class实例
Class类
Class类的常用方法
如何获取Class类的实例
案例
package com.jt.reflection;
/**
* @author CMJ
* 2021/6/14 17:59
* 测试Class类的创建方式有哪些
*/
public class GetClass {
public static void main(String[] args) {
Person person=new Student();
System.out.println("这个人是:"+person.name);
//这个人是:学生
//方式一: 通过对象获得
Class<? extends Person> c1 = person.getClass();
System.out.println(c1.hashCode());
//460141958
//方式二: 通过静态方法 forName ("类路径") 获得
try {
Class<?> c2 = Class.forName("com.jt.reflection.Student");
System.out.println(c2.hashCode());
//460141958
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//方式三; 通过 类名.class 获得
Class<Student> c3 = Student.class;
System.out.println(c3.hashCode());
//460141958
//方式四: 基本内置类型的包装类都有一个TYPE属性
Class<Integer> c4 = Integer.TYPE;
System.out.println(c4);
System.out.println(c4.hashCode());
//int
//1163157884
//获得父类类型
Class<?> c5 = c1.getSuperclass();
System.out.println(c5);
//class com.jt.reflection.Person
}
}
class Person{
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public Student() {
this.name="学生";
}
}
class Teacher extends Person{
public Teacher() {
this.name="老师";
}
}
哪些类型可以有class对象?
/**
* @author CMJ
* 2021/6/14 18:33
* 测试哪些类型有Class对象
*/
public class WhoHaveClass {
public static void main(String[] args) {
//类 class java.lang.Object
Class c1 = Object.class;
//接口 interface java.lang.Comparable
Class c2 = Comparable.class;
//数组 class [Ljava.lang.String;
Class c3 = String[].class;
//二维数组 class[[I
Class c4 = int[][].class;
//注解 interface java.lang.Override
Class c5 = Override.class;
//枚举 class java.lang.annotation.ElementType
Class c6 = ElementType.class;
//基本数据类型 class java.lang.Integer
Class c7 = Integer.class;
//void 代表一个空类型 void
Class c8 = void.class;
//Class 本身就是一个类 class java.lang.Class
Class c9 = Class.class;
/**只要元素类型与维度一样,就是同一个class对象
* 一个类只有一个class对象
*/
int[] a = new int[10];
int[] b = new int[100];
System.out.println(a.getClass().hashCode());
//460141958
System.out.println(b.getClass().hashCode());
//460141958
}
}
2.3 类的加载与ClassLoader
类的加载过程
package com.jt.reflection;
/**
* @author CMJ
* 2021/6/14 19:24
* 类是如何加载的?
* 类中的变量初始化发生了什么
*
* 1.加载到内存 , 产生一个类对应的class对象 , 每个类都拥有一个自己的class对象
* 2.链接 链接结束后 m=0 , 为类变量设置初始值
* 3.初始化 < clinit >(){
* 根据类变量的赋值顺序合并所有赋值动作
* static int m =100;
* m=300;
* }
* 赋值完成,初始化完成 m=300
* */
public class ClassInit {
public static void main(String[] args) {
A a = new A();
System.out.println(A.m);
}
}
class A{
static int m =100;
static {
System.out.println("A类的静态代码块初始化");
m=300;
}
public A(){
System.out.println("A类的无参构造初始化");
}
}
什么时候发生类的初始化
/**
* @author CMJ
* 2021/6/14 20:23
*
* 测试类什么时候会初始化
*
* 类的主动引用和被动引用
*/
public class ActiveReference {
static {
System.out.println("main类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
//1.主动引用
Son son = new Son();
/**
* main类被加载
* 父类被加载
* 子类被加载
*/
//反射也是主动引用
Class<?> son = Class.forName("com.jt.reflection.Son");
/**
* main类被加载
* 父类被加载
* 子类被加载
* 极大的消耗资源
*/
//2.被动引用
//子类调用父类的静态方法或变量,不会导致子类被初始化
System.out.println(Son.b);
/**
* main类被加载
* 父类被加载
* 2
*/
//通过数组定义类引用,不会触发此类的初始化
Son[] array = new Son[10];
/**
* main类被加载
*/
//引用常量
System.out.println(Son.M);
/**
* main类被加载
* 1
*
* 因为所有的常量还有静态变量在类的链接阶段就已经设置默认初始值
* 常量在链接阶段就存入了调用类的常量池中了
* 静态变量可以重新赋值,而常量值不能被改变
* 所以引用此类的常量不会触发此类的初始化
*/
}
}
class Father{
static int b = 2;
static {
System.out.println("父类被加载");
}
}
class Son extends Father{
static {
System.out.println("子类被加载");
m=300;
}
static int m=100;
static final int M=1;
}
ClassLoader 类加载器
- java核心库---------rt.jar
- java扩展包---------jre/lib/ext
程序不是凭空运行,由类加载器加载到内存
- 双亲委派机制
自定义类时,不能与java核心包重名,加载自定义类时,加载器会层层向上级核实—>系统类加载器—>扩展类加载器—>引导类加载器 如自定义java.lang.String类 无法加载运行,以保证java运行环境的安全性
package com.jt.reflection;
/**
* @author CMJ
* 2021/6/14 21:03
* 获取系统类加载器
*/
public class GetClassLoader {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器---用户的一个类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//获取系统类加载器的父类加载器---扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
//获取扩展类加载器的父类加载器---根加载器(C/C++编写)无法打印
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
/**
* sun.misc.Launcher$AppClassLoader@18b4aac2
* sun.misc.Launcher$ExtClassLoader@1b6d3586
* null
*/
//测试当前类是哪个加载器加载的
ClassLoader classLoader = Class.forName("com.jt.reflection.GetClassLoader").getClassLoader();
System.out.println(classLoader);
//测试JDK内置的类是谁加载的
ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader1);
/**
* sun.misc.Launcher$AppClassLoader@18b4aac2
* null
*/
// .sout 可以把写好的代码打印
//获得系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
/** 运行环境jar包
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\charsets.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\deploy.jar;
* 扩展类jar包
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\access-bridge-64.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\cldrdata.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\dnsns.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\jaccess.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\jfxrt.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\localedata.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\nashorn.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\sunec.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\sunjce_provider.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\sunmscapi.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\sunpkcs11.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\zipfs.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\javaws.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\jce.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\jfr.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\jfxswt.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\jsse.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\management-agent.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\plugin.jar;
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\resources.jar;
* java核心库jar包
* C:\Program Files\Java\jdk1.8.0_211\jre\lib\rt.jar;
* 本类jar包
* E:\Pritices\JavaBase_Reflection\target\classes;
* E:\softwear\idea\IntelliJ IDEA 2021.1\lib\idea_rt.jar
*/
}
}
2.4 创建运行时类的对象
获取运行时类的完整结构
package com.jt.reflection;
/**
* @author CMJ
* 2021/6/14 21:55
* 获取类的信息
*/
public class GetClassInformation {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class<?> c1 = Class.forName("com.jt.pojo.User");
/* User user = new User();
Class<? extends User> c1 = user.getClass();*/
System.out.println("获取类的全类名:"+c1.getName());
//com.jt.pojo.User 包名.类名
System.out.println("获取类的简单名称:"+c1.getSimpleName());
//User 类名
System.out.println("==============================================");
//获得类的属性
Field[] fields = c1.getFields();
//只能找到public属性
//fields.for 直接循环打印
for (Field field : fields) {
System.out.println(field);
}
Field[] fields1 = c1.getDeclaredFields();
//可以找到全部属性
for (Field field : fields1) {
System.out.println(field);
}
/**
* private java.lang.Integer com.jt.pojo.User.id
* private java.lang.String com.jt.pojo.User.name
*/
//获取指定属性
System.out.println(c1.getDeclaredField("name"));
System.out.println("==============================================");
//获取类的方法
Method[] methods = c1.getMethods();
//获取本类及父类的所有public方法
for (Method method : methods) {
System.out.println("getMethods:"+method);
}
//获取本类所有的public和private方法
Method[] methods1 = c1.getDeclaredMethods();
for (Method method : methods1) {
System.out.println("getDeclaredMethods"+method);
}
//获得指定方法
//java中有方法的重载,所以在获取指定方法是需要指定参数类型
Method getName = c1.getDeclaredMethod("getName", null);
System.out.println(getName);
Method setName = c1.getMethod("setName", String.class);
System.out.println(setName);
/**
* public java.lang.String com.jt.pojo.User.getName()
* public void com.jt.pojo.User.setName(java.lang.String)
*/
System.out.println("==============================================");
}
}
动态创建对象执行方法
- 创建独享
- 调用指定方法
- 测试
package com.jt.reflection;
/**
* @author CMJ
* 2021/6/14 22:37
* 通过反射,动态创建对象,调用方法和属性
*/
public class GetInstance {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class<?> c1 = Class.forName("com.jt.pojo.User");
User user = (User) c1.newInstance();
System.out.println(user);
/**
* User{id=null, name='null'}
* 该方法本质上调用无参构造创建对象
*/
//通过构造器创建对象
Constructor<?> Constructor = c1.getDeclaredConstructor(Integer.class, String.class);
User user2 = (User) Constructor.newInstance(18, "爱我中华");
System.out.println(user2);
/**
* User{id=18, name='爱我中华'}
* 构造器可通过有参构造创建User对象
*/
User user3= (User) c1.newInstance();
//通过反射调用普通方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke:激活的意思 需要传递的参数("哪个对象的方法","方法的参数值")
setName.invoke(user3,"猪八戒");
System.out.println(user3.getName());
/**
* 猪八戒
*/
//通过反射操作属性
User user4= (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
name.setAccessible(true);
name.set(user4,"孙悟空");
System.out.println(user4.getName());
/**
* can not access a member of class
* com.jt.pojo.User with modifiers "private"
* 可以获取私有属性信息,但是不能直接操作私有属性
* 需要关闭关闭程序的安全检测,操作私有属性和方法通用的
*
* 孙悟空
*/
}
}