文章目录
Java注解与反射
注解(Annotation)
注解是JDK5.0引入的一个技术。
作用
- 不是程序本身,可以对程序做出解释(这点功能与注释相同)
- 可以被其他程序读取(可以作为功能加入程序中)
格式
-
注解以”@注释名“再代码中存在,还可以添加一些参数值。
@SuppressWarnings(value=”unchecked“)
内置注解
- @Override:重写
- @Deprecated:表示不鼓励程序员使用
- @SuppressWainings:用来抑制警告。就是在编译器中编码的时候不会爆出警告
自定义注解
- 使用 @interface 定义类后该类就是注解了。
// TYPE:类; METHOD:方法; FIELD:属性
@Target(value="ElementType.METHOD")
@Retention(value="RetentionPolicy.RUNTIME")
public @interface MyAnnotation{
// 这是注解的参数(),如果没有默认值就必须赋值,否则注解报错
String name();
String sex() default "xxx";
int age() default 0;
String[] schools() defaule {"xxx", "xxx"};
}
元注解
作用:用来注解其他注解的注解。用来对其他Annotation类型做说明。
-
@Target:描述了注解的使用范围
@Target(value="ElementType.METHOD") // 或者可以这样@Target([ElementType.METHOD, ElementType.TYPE])表示多个作用域 // ElementType是枚举类型,其中属性有类、方法等。这里METHOD就是指这个注解只能用在方法上 public @interface MyAnnotation{ }
-
@Retention:表示什么级别保存该注释信息,用于描述注解的声明信息
@Retention(value="RetentionPolicy.RUNTIME") // RetentionPolicy是枚举类型,RUNTIME指运行时注解有效。 public @interface MyAnnotation{ }
-
@Documentd:表示是否将我们的注解生成在JAVAdoc中
@Documentd // 表示是否将我们的注解生成在JAVAdoc中 public @interface MyAnnotation{ }
-
@Inherited:表示子类是否继承父类注解
@Inherited // 表示子类是否继承父类注解 public @interface MyAnnotation{ }
反射
使java具有动态性的一个技术!使用反射后可以使Java编程准动态语言。
动态语言:
- 动态语言是一类可以在运行时,改变其结构的语言。就是在运行时代码可以根据某些条件改变自身结构。
- 主要的动态语言有:PHP、JavaScript、Python
静态语言:
- 运行时结构不可改变:例如Java、c++、c
反射的定义
反射是Java被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API获取任何类的内部信息,并能直接操作任意对象的内部属性及方法!
Class c = Class.forName("java.lang.String");
// Class类是用来管理Reflection API的
// Object类拥有getClass方法用来获取类的结构,所以所有类都能获取类的结构
Object obj = new Object();
Class objClass = obj.getClass();
加载完类后, 在堆内存就产生了一个Class类型的对象(一个类只能有一个Class对象),该对象包含类的完整结构信息。 我们可以通过这个类看到类的结构,这个对象就像以面镜子可以看到类的结构,所以我们叫反射。
Class类
- Class本身也是一个类
- Class对象只能由系统建立对象
- 一个加载的类在JVM中只会有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由那个Class实例所生成的
- 通过Class可以完整的得到一个类中的所有被加载的结构
- Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。
Class类常用方法:
方法名 | 功能说明 |
---|---|
static Class ClassforName(String name) | 返回指定类名name的Class对象 |
Object newInstance() | 调用缺省构造方法,返回Class对象的一个实例 |
getName() | 返回Class对象多表示的实体的名称 |
Class getSuperClass() | 返回当前Class对象的父类Class对象 |
Class[] getinterfaces() | 获取当前Class对象的接口 |
CassLoader getClassLoader() | 返回该类的类加载器 |
Constructor[] getConstructors() | 返回一个包含某些Constructor对象的数组 |
Method getMethod(String name, Cass…T) | 返回一个Method对象,此对象的形参类型为paramType |
Field[] getDeclaredFields() | 返回Field对象的一个数组 |
获取Class对象:
-
已知具体类,通过class属性获得
Class c = Person.class;
-
已知某个类的实例,通过getClass方法获得
Person person = new Person(); Class c = person.getCalss();
-
已知一个类的全类名,通过Cass.forName获得
Class c = Class.forName("com.qiu,Person");
类加载内存分析
- 加载:将class文件字节码加载到内存中,并将这些静态数据转换成方法区的运行是数据结构(Class对象存放在方法区),然后生成一个代表这个类的java.lang.Class对象
- 链接:将Java类的二进制代码合并到JVM的运行状态中
- 验证:确保加载的类的信息符合JVM规范
- 准备:正式为类变量分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配
- 装入:JVM常量池中的符号引用替换为直接地址的过程
- 初始化:
- 将所有静态代码块与所有类变量的赋值动作合并为一个类构造器< clinit >(),并执行
- 当初始化一个类的时候,如果发现其父类还没有初始化,则先触发其父类的初始化
- 虚拟机会保证一个类的< clinit >()方法在对线程环境中被正确加锁和同步
public class Person{
static {
System.out.println("静态代码块")
m = 300;
}
static int m = 100;
public Person(){
System.out.println("无参构造")
}
}
public class Test{
public static void main(String[] args){
Person a = new Person();
Systen.out.println(Person.m);
}
}
结果:
静态代码块
无参构造
100
类加载器
类加载器作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆内存中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
类缓存:标准javaSE类加载器可以按照要求查找相应类,但一旦某个类被加载到类加载器中,它将维持加载一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
类加载器需要加载路径,将指定路径下的包、class文件加载到内存中。加载后程序才能运行。
所以有一个双亲委派机制,保证根加载器加载路径中的包不会被覆盖。例如你要写一个java.lang.String类
那么这个类是不能被类加载器加载的(从系统加载器向父级查找是否有这个包,有则不被加载),不被加载就不会运行。
类加载器分为:
- 引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。
- 扩展类加载器:负责JRE/lib/ext目录下的jar包或-D java.ext.dirs指定目录下的jar包装入工作库
- 系统类加载器:负责java -classpath或-D java.class.path所致的目录下的类与jar包装入工作,是最常见的加载器
三者关系为:引导类加载器(根加载器)==----是父类---->扩展类加载器----是父类---->==系统类加载器
ClassLoader systemClassLoader = ClassLoader.getClassLoader();
ClassLoader extClassLoader = systemClassLoader.getParent();
ClassLoader rootClassLoader = extClassLoader.getParent();
获取类对象的类加载器:
ClassLoader loader = Class.forName("com.qiu.pojo.Student").getClassLoader();
获取类的信息
Class c = Class.forName("com.qiu.pojo.Student");
-
获取类的名字:
// 全类名 String name = c.getName(); // 类名 String simpleName = c.getSimpleName();
-
获取属性:
// 获取所有public属性 Field[] fields = c.getFields(); // 获取所有属性 Field[] fields = c.getDeclaredFields(); // 获取指定public属性 Field field = c.getField("name"); // 获取指定属性 Field field = c.getDeclaredField("name"); //----------------------------------------------------- // 获取方法以及父类及以上方法 Method[] methods = c.getMethods(); // 获取本类的方法 Method[] method = c.getDeclaredMethods(); // 获取指定方法("方法名", 参数值类) Method getName = c.getMethod("getName", null); Method setName = c.getMethod("setName", String.class); //----------------------------------------------------- //获取public构造器 Constructor[] constructors = c.tgetConstructors(); //获取所有构造器 Constructor[] constructors = c.tgetDeclareConstructors(); //获取指定构造器 Constructor declaredConstructor = c.getDeclaredConstructor(String.class, int.class, int.class);
动态创建对象(通过反射操作对象)
通过类对象创建对象
Class c = Class.forName("com.qiu.pojo.Student");
Student s = (Student)c.newInstance();
这样创建对象调用的是无参构造
- 类必须有一个无参构造器
- 类的构造器的访问权限需要足够
通过构造器创建对象
Class c = Class.forName("com.qiu.pojo.Student");
Constructor constructor = c.getDeclaredConstructor(String.class);
Student s = (Student)constructor.newInstance("hahaha")
通过反射获取方法, 再进行调用
直接使用对象的方法不好, 需要通过反射获取方法, 然后调用
Class c = Class.forName("com.qiu.pojo.Student");
Student s = (Student)c.newInstance();
Method setName = c.getMethod("setName", String.class);
// 调用(对象, 参数), 对象表示是哪个对象调用的
setName.invoke(s, "jajaja");
通过反射获取属性
Class c = Class.forName("com.qiu.pojo.Student");
Student s = (Student)c.newInstance();
Field name = c.getDeclaredField("name");
// 取消安全检测 只有对自由属性赋值/获取的时候需要这个,因为私有属性是不可访问的
name.setAccessilble(true);
// 对对象s的私有属性进行赋值
name.set(s, "qiqiqi");
// 获取对象s的name属性
String name_string = (String)name.get(s)
Method, Field, Constructor对象都有一个setAccessible方法用于启动/解除安全检测!
通过反射操作注解
@Target(ElementType.TYPE)
@Retention(RetenrionPolicy.RUNTIME)
@interface ClassAnnotation{
String value();
}
@ClassAnnotation(value="MyClass")
class Student{
private String name;
}
Class c = Class.forName(com.qiu.reflection.ClassAnnotation);
// 获取类的所有指定注解
Annotation[] annotation = c.getAnnotation();
// 获取指定注解
ClassAnnotation annotation = (ClassAnnotation)c.getAnnotation(ClassAnnotation.class);
String value = annotation.value();