反射与注解
反射
- 框架的设计灵魂,框架半成品框架,可以在框架的基础上开发,简化编码
- 将类的各个组成部分,封装为对象,这就是反射机制
-
好处
- 可以在程序运行过程中操作这些文件
- 可以解耦,提高程序的可扩展性
-
Field成员变量
- 获取值,Object get(Object obj)
- 设置值,void set(Object obj, Object value)
- 暴力反射,忽略安全检查修饰符的检查,不然会报错,setAccessible(true)
获取成员变量
java">package reflect;
import java.lang.reflect.Field;
/*
Class对象功能
获取功能
获取成员变量
Field getField(String name) 获取指定名称public修饰的成员变量
Field[] getFields() 获取所有public修饰的成员变量
Field getDeclaredField(String name) 获取指定成员变量,不考虑修饰符
Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
获取构造方法
Constructor<?>[] getConstructors()
Constructor<T> getConstructor(类<?>... parameterTypes)
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes
Constructor<?>[] getDeclaredConstructors()
获取成员方法们
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, 类<?>... parameterTypes)
Method getMethod(String name, 类<?>... parameterTypes)
Method[] getMethods()
获取类名
String getName()
*/
public class Demo01 {
public static void main(String[] args) throws Exception {
//获取Person的Class对象
Class<Person> personClass = Person.class;
//Field getField(String name) 获取所有public修饰的成员变量
Field[] field = personClass.getFields();
for (Field field1 : field) {
System.out.println(field1);
}
System.out.println("====================================");
//Field getField(String name) 获取指定名称public修饰的成员变量
Field field1 = personClass.getField("a");
//获取a的值
Person person = new Person();
Object o = field1.get(person);
System.out.println(o);
//设置a值
field1.set(person,"zhangsan");
System.out.println(person);
System.out.println("====================================");
//Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("====================================");
//Field getDeclaredField(String name) 获取指定成员变量,不考虑修饰符
Field d = personClass.getDeclaredField("d");
//忽略安全检查修饰符的检查,不然会报错
d.setAccessible(true);//暴力反射
Object o1 = d.get(person);
System.out.println(o1);
}
}
获取构造方法
java">package reflect;
import java.lang.reflect.Constructor;
/*
获取构造方法,用来创建对象
Constructor<?>[] getConstructors()
Constructor<T> getConstructor(类<?>... parameterTypes)
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes
Constructor<?>[] getDeclaredConstructors()
*/
public class Demo02 {
public static void main(String[] args) throws Exception {
//获取Person的Class对象
Class<Person> personClass = Person.class;
//Constructor<T> getConstructor(类<?>... parameterTypes)
Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
System.out.println(constructor);
//创建对象newInstance()
Person person = constructor.newInstance("张三", 23);
System.out.println(person);
}
}
动态语言与静态语言
- 动态语言,是一类运行时可以改变其结构的语言,例如新的函数、对象、代码被引进。已有函数可以被删除或者其他结构上的变化。
- 静态语言,运行时结构不可变的语言
- java不是动态语言,但是java可以称为准动态语言,java有一定的动态性,我们可以利用反射机制获取类似动态语言动态特性。
java_122">java反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员方法和变量
- 在运行时调用任意一个对象的成员方法和变量
- 在运行时获取泛型信息
- 在运行时处理注解
- 生成动态代理
- 等
反射的优缺点
- 优点,可以实现动态创建对象和编译,体现出很大的灵活性
- 缺点,对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。
反射相关的主要API
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造器
Class类
- 对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息)
- Class 本身也是一个类
- Class 对象只能由系统建立对象
- 一个加载的类在JVM中只会有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一 个.class文件
- 每个类的实例都会记得自己是由哪个Class实例所生成
- 通过Class可以完整地得到一个类中的所有被加载的结构
- Class类 是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
java">package reflect;
//测试class类的创建方式有哪些
public class Demo04 {
public static void main(String[] args) throws ClassNotFoundException {
person s1 = new student();
System.out.println("zhegerenshi"+s1);
//方式一,通过对象获得
Class c1 = s1.getClass();
System.out.println(c1.hashCode());
//方式二,forname通过包名
//Class c2 = Class.forName("reflect.Demo04.student");
//System.out.println(c2.hashCode());
//方式三,通过类名获得
Class<student> studentClass = student.class;
System.out.println(studentClass.hashCode());
//方式四,基本类型的包装类都有一个TYPE属性
Class<Integer> type = Integer.TYPE;
System.out.println(type);
//获取父类类型
Class superclass = c1.getSuperclass();
System.out.println(superclass);
}
}
class person{
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对象
- class:外部类,成员内部类,静态内部类,局部内部类,静态内部类
- interface:接口
- []:数组
- enum:枚举
- annotation: 注解@interface
- primitive type:基本数据类型
- void
java">package reflect;
import java.lang.annotation.ElementType;
//所有类型的class
public class Demo05 {
public static void main(String[] args) {
Class<Object> objectClass = Object.class;//类
Class<Comparable> comparableClass = Comparable.class;//接口
Class<String[]> aClass = String[].class;//一维数组
Class<int[][]> aClass1 = int[][].class;//二维数组
Class<Override> overrideClass = Override.class;//注解
Class<ElementType> elementTypeClass = ElementType.class;//枚举
Class<Integer> integerClass = Integer.class;//基本数据类型
Class<Void> voidClass = void.class;//空
Class<Class> classClass = Class.class;//Class
System.out.println(objectClass);
System.out.println(comparableClass);
System.out.println(aClass);
System.out.println(aClass1);
System.out.println(overrideClass);
System.out.println(elementTypeClass);
System.out.println(integerClass);
System.out.println( voidClass);
System.out.println(classClass);
//只要元素类型与维度一样,就是同一个class
int[] a={10};
int[] b={100};
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
}
}
类的加载与ClassLoader
-
加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象.
- 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
- 验证: 确保加载的类信息符合JVM规范,没有安全方面的问题
- 准备:正式为类变量(static) 分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
-
初始化:
-
执行类构造器 ()方法的过程。类构造器< clinit> ()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造 器是构造类信息的,不是构造该类对象的构造器)。
-
当初始化- 个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
-
虚拟机会保证一-个类的 ()方法在多线程环境中被正确加锁和同步。
java">package reflect;
/*
1.加载到内存,会产生每一个类对应的class对象
2.链接,链接结束 m=0
3.初始化
<clinit>(){
System.out.println("A类静态代码块初始化");
m=300;
m=100;
}
m==100;
*/
public class Demo06 {
public static void main(String[] args) {
A a = new A();
System.out.println(a.m);
}
}
class A{
static {
System.out.println("A类静态代码块初始化");
m=300;
}
static int m=100;
public A() {
System.out.println("A类的无参构造器");
}
}
什么时候会初始化
-
类的主动引用(- 定会发生类的初始化)
-
类的被动引用(不会发生类的初始化)
- 当访问- -个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
- 通过数组定义类引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
类加载器的作用
- 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
- 类缓存:标准的JavaSE类加载器可以按要求查找类,但某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象
java">package reflect;
public class Demo07 {
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++编写),获取不到返回null
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
//测试当前类是哪个加载器,加载的
ClassLoader aClass = Class.forName("reflect.Demo07").getClassLoader();
System.out.println(aClass);
//测试JDK内置的类,是谁加载的
ClassLoader classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);
//如何获取系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
}
}
java">package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
//获取类的信息
public class Demo08 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class<?> c1 = Class.forName("reflect.User");
/*//通过对象就能获得包名加类名
User user = new User();
c1=user.getClass();*/
//获得类的名字
System.out.println(c1.getName());//reflect.User
//获取类的简单名字
System.out.println(c1.getSimpleName());//User
//获得类的public属性
Field[] fields = c1.getFields();//获取本类与父类的public方法
for (Field field : fields) {
System.out.println(field);
}
//获得类的所有属性
Field[] declaredFields = c1.getDeclaredFields();//获取本类的方法
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
/*//获得指定public属性
Field name = c1.getField("name");
System.out.println(name);*/
//获得指定public属性
Field name1 = c1.getDeclaredField("name");
System.out.println(name1);
System.out.println("=============================================");
//获取类的方法
Method[] methods = c1.getMethods();
for (Method method : methods) {
System.out.println("getMethods()======="+method);
}
Method[] declaredMethods = c1.getDeclaredMethods();
for (Field declaredField : declaredFields) {
System.out.println("getDeclaredMethods()========"+declaredField);
}
//获取指定的方法
Method getId = c1.getMethod("getId", null);//填(方法名,参数,无参传null),因为有重载
System.out.println(getId);
//获得构造器,构造方法
Constructor[] constructors = c1.getConstructors();//public
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
Constructor[] declaredConstructors = c1.getDeclaredConstructors();//All
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
//获得指定构造器
Constructor constructor = c1.getConstructor(String.class, int.class, int.class);
System.out.println(constructor);
}
}
有了class能做什么
- 创建类的对象:调用Class对象的newInstance()方法
- 类必须有一-个无参数的构造器。
- 类的构造器的访问权限需要足够
无参的构造器创建对象
-
操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。
-
步骤如下:
- 通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构
造器 - 向构造器的形参中传递一一个对象数组进去, 里面包含了构造器中所需的各个参数。
- 通过Constructor实例化对象
- 通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构
java">package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
//动态的创建对象,通过反射
public class Demo09 {
public static void main(String[] args) throws Exception {
//获得class对象
Class c1 = Class.forName("reflect.User");
//构造一个对象
User user = (User)c1.newInstance();//本质是调用了类的无参构造器
System.out.println(user);
//通过构造器创建对象
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
User user2 = (User) declaredConstructor.newInstance("鱼汤", 1818, 18);
System.out.println(user2);
//通过反射调用方法
User user3 = (User)c1.newInstance();
//通过反射获取方法
/*
invoke,激活的意思
(对象,方法的值)
*/
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(user3,"yutang");
System.out.println(user3.getName());
//通过反射操作属性
User user4 = (User)c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作程序的私有属性,我们要关闭程序的安全检测,通过属性和程序的setAccessible(true);
name.setAccessible(true);//会降低程序的安全性
name.set(user4,"yutang2");
System.out.println(user4.getName());
}
}
setAccessible
-
Method和Field、Constructor对象都有setAccessible()方法。
-
setAccessible作用是启动和禁用访问安全检查的开关。
-
参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
-
提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。
- 使得原本无法访问的私有成员也可以访问
-
参数值为false则指示反射的对象应该实施Java语言访问检查
反射操作泛型
- Java采用泛型擦除的机制来引入泛型, Java中的泛型仅仅是给编译器javac使用的,确保数据
- 的安全性和免去强制类型转换问题,但是, -旦编译完成,所有和泛型有关的类型全部擦除
- 为了通过反射操作这些类型, Java新增了ParameterizedType , GenericArrayType,
- TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型.
- ParameterizedType :表示- -种参 数化类型,比如Collection
- GenericArrayType :表示一 种元素类型是参数化类型或者类型变量的数组类型
- TypeVariable :是各种类型变量的公共父接口
- WildcardType :代表一种通配符类型表达式
java">package reflect;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
//通过反射获取泛型
public class Demo10 {
public void test01(Map<String,User> map, List<User> list){
System.out.println("test01");
}
public Map<String,User> test02(){
System.out.println("test02");
return null;
}
public static void main(String[] args) throws Exception {
Method method = Demo10.class.getDeclaredMethod("test01", Map.class, List.class);
//getGenericParameterTypes,获取泛型的参数类型
Type[] genericExceptionTypes = method.getGenericParameterTypes();
for (Type genericExceptionType : genericExceptionTypes) {
System.out.println(genericExceptionType);
if(genericExceptionType instanceof ParameterizedType){
//getActualTypeArguments,获取真实的参数类型
Type[] actualTypeArguments = ((ParameterizedType) genericExceptionType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
//获取返回值类型
method = Demo10.class.getDeclaredMethod("test02",null);
Type genericReturnType = method.getGenericReturnType();
if(genericReturnType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
反射操作注解
- ORM, Object relationship Mapping -->对象关系映射
java">package reflect;
import java.lang.annotation.*;
//练习反射操作注解
public class Demo11 {
public static void main(String[] args) throws Exception {
Class c1 = Class.forName("reflect.Student2");
//通过反射获得注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得注解value的值
Table annotation = (Table) c1.getAnnotation(Table.class);
String value = annotation.value();
System.out.println(value);
///获得类指定注解
java.lang.reflect.Field f = c1.getDeclaredField("name");
Field annotation1 = f.getAnnotation(Field.class);
System.out.println(annotation1.columnName());
System.out.println(annotation1.type());
System.out.println(annotation1.length());
}
}
@Table("DB_student")
class Student2{
@Field(columnName = "DB_id",type = "int",length = 10)
private int id;
@Field(columnName = "DB_age",type = "int",length = 10)
private int age;
@Field(columnName = "DB_name",type = "varcher",length = 3)
private String name;
public Student2() {
}
public Student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field{
String columnName();
String type();
int length();
}
注解
-
Annotation是从JDK5.0开始的新技术
-
不是程序本身,但能对程序做出解释,可以被其他程序读取
-
注解是以“@注释名”在代码中存在的,还可以添加一些数值
-
常见内置注解
- @Override,重写方法
- @Deprecated,废弃
- @SuppressWarnings,用来抑制编译时的警告信息
元注解,负责注解其他注解
- @Target,用于描述注解的使用范围(后面参数里设置)
- @Retention,表示在什么级别保存注解信息,用于描述注解生命周期
- runtime>class>sourdes
- @Document,说明该注解将被包含在javadoc中
- @Inherited,说明子类可以继承父类的注解
自定义注解
- 使用@Inherited自定义注解时,自动继承java.lang.annotation.Annotation接口
- @Inherited用来申明一个注解,格式,public @Inherited注解名{定义内容}
- 其中每一个方法,实际上是申明了一个配置参数
- 方法的名称就是参数的名称
- 返回值类型就是参数的类型(只能是Class、String、enum)
- 可以通过default来申明参数的默认值
- 如果只有一个参数成员,一般参数名为value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串、0作为默认值
java">package reflect;
import java.lang.annotation.*;
//自定义注解
public class ZhuJie {
//注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
@MyAnnotation(name="yutang",school = "西邮")
public void test(){
}
@MyAnnotation02("gdsygsu")//只有一个参数,且在注解中用value命名,可以直接写所赋值
public void test02(){}
}
@Target({ElementType.TYPE,ElementType.METHOD})//作用域,类、方法
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
//注解的参数 参数类型+参数名+();
String name();
//String name() default "";//default ""默认空参数,使用注解是不用传递值
int age() default 0;
int id() default -1;//如果默认值为-1,代表不存在,indexof如果找不到返回-1
String[] school() default "清华";
}
@Target({ElementType.TYPE,ElementType.METHOD})//作用域,类、方法
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation02{
String value();
}