概述 反射机制的作用 可以操作字节码文件 可以读和修改字节码文件 反射机制,让代码很具有通用性,可变化的内容都是写到配置文件当中,将来修改配置文件之后,创建的对象不一样了,调用的方法也不同了,但是java代码不需要做任何改动。这就是反射机制的魅力。
反射机制相关类的包 java.lang.reflect*;
反射机制相关类 java.lang.Class:代表字节码文件 java.lang.reflect.Method:代表字节码中的方法字节码 java.lang.reflect.Constructor:代表字节码中的而构造方法字节码 java.lang.reflect.Field:代表字节码中的属性字节码
获取Class的三种方式 public static Class<?> forName(String className) throws ClassNotFoundException
静态方法
方法的参数是―个字符串。
字符串需要的是一个完整类名。
完整类名必须带有包名。java.lang包也不能省略。1 2 3 4 5 6 7 8 9 10 11 12 public class _652_ 获取Class 的三种方式 { public static void main (String[] args) { try { Class c1 = Class.forName("java.lang.String" ); Class c2 = Class.forName("java.lang.Integer" ); Class c3 = Class.forName("java.util.Date" ); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
public final native Class<?> getClass() 运行时Class文件在JVM的方法区中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class _653_ 获取Class 的三种方式 { public static void main (String[] args) { Class c1 = null ; try { c1 = Class.forName("java.lang.String" ); } catch (ClassNotFoundException e) { e.printStackTrace(); } String s = "abc" ; Class x = s.getClass(); System.out.println(c1 == x); } }
任何一种数据类型都有.class属性 1 2 3 4 5 6 7 8 9 10 import java.util.Date;public class _654_ 获取Class 的三种方式 { public static void main (String[] args) { Class x = String.class; Class y = Date.class; Class z = int .class; } }
通过反射实例化对象 1 public T newInstance () throws InstantiationException, IllegalAccessException
必须保证无参构造是存在的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class _655_ 通过反射实例化对象 { public static void main (String[] args) { try { Class c1 = Class.forName("User" ); Object o = c1.newInstance(); System.out.println(o); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } class User { public User () { System.out.println("无参构造方法执行~" ); } public User (String s) { System.out.println("有参构造方法执行~" ); } }
通过读属性文件实例化对象 验证反射机制的灵活性 java代码写一遍,再不改变java源代码的基础之上,可以做到不同对象的实例化。非常之灵活。(符合OCP开闭原则:对扩展开放,对修改关闭。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import java.io.FileInputStream;import java.util.Properties;public class _656_ 通过读属性文件实例化对象 { public static void main (String[] args) throws Exception { FileInputStream fis = null ; fis = new FileInputStream("【651-685】反射机制/src/_656_通过读属性文件实例化对象.properties" ); Properties mp = new Properties(); mp.load(fis); String s = mp.getProperty("className" ); System.out.println(s); Class<?> c = Class.forName(s); Object o = c.newInstance(); System.out.println(o); fis.close(); } } class User2 {}
使用forName可以只让类的静态代码块执行 导致类加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class _657_ 只让静态代码块执行可以使用forName { public static void main (String[] args) { try { Class<?> c = Class.forName("MyClass" ); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } class MyClass { static { System.out.println("静态代码块执行~" ); } }
获取类路径下文件的绝对路径 什么是类路径 src下的都是类路径,src是类的根路径 IDEA中其实是在out/production下
Thread.currentThread().getContextClassLoader().getResource(“”).getPath() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class _658_ 获取类路径下文件的绝对路径 { public static void main (String[] args) { String path = Thread .currentThread() .getContextClassLoader() .getResource("_653_获取Class的三种方式.class" ) .getPath(); System.out.println(path); String path2 = Thread .currentThread() .getContextClassLoader() .getResource("_656_通过读属性文件实例化对象.properties" ) .getPath(); System.out.println(path2); } }
Thread.currentThread().getContextClassLoader().getResourceAsStream(“”) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import java.io.FileInputStream;import java.io.InputStream;import java.util.Properties;public class _659_ 以流的形式直接返回 { public static void main (String[] args) throws Exception { InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("_659_以流的形式直接返回.properties" ); Properties mp = new Properties(); mp.load(is); System.out.println(mp.getProperty("className" )); } }
资源绑定器 便于获取属性配置文件中的内容 使用以下这种方式的时候,属性配置文件 xxx.properties 必须放在类路径下 写路径是只能写 “xxx” 不能加扩展名
1 2 3 4 5 6 7 8 9 10 11 12 13 import java.util.ResourceBundle;public class _660_ 资源绑定器 { public static void main (String[] args) { ResourceBundle bundle = ResourceBundle.getBundle("_660_资源绑定器" ); System.out.println(bundle); String s = bundle.getString("className" ); System.out.println(s); } }
类加载机制 什么是类加载器 专门负责加载类的命令/工具 ClassLoader
JDK自带3个类加载器
启动类加载器
扩展类加载器
应用类加载器
如果自己写了一个类String,植入java.lang.String,那么首先加载启动类加载器”父”也就是java本身的String类,再从扩展类加载器加载”母”,这是双亲委派机制 最后从应用类加载器加载,也就是自己写的类
example
首先通过启动类加载器,寻找String.class,专门加载/jre/lib/rt.jar下的class文件 如果找不到则扩展类加载器寻找,专门加载/jre/lib/ext/*.jar下的class文件 如果还找不到则应用类加载器寻找,专门加载classpath中的jar包(计算机中的”环境变量classpath”)
Field 获取Field 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import java.lang.reflect.Field;import java.lang.reflect.Modifier;public class _664_ 获取Field { public static void main (String[] args) throws ClassNotFoundException { Class<?> student = Class.forName("Student" ); Field[] fields = student.getFields(); System.out.println(fields.length); System.out.println(fields[0 ]); System.out.println(fields[0 ].getName()); Field[] declaredFields = student.getDeclaredFields(); System.out.println(declaredFields.length); for (int i = 0 ; i < declaredFields.length; i++) { System.out.println(declaredFields[i]); System.out.println(declaredFields[i].getModifiers() + "->" + Modifier.toString( declaredFields[i].getModifiers() ) ); System.out.println(declaredFields[i].getType()); System.out.println(declaredFields[i].getName()); } } } class Student { private String name; boolean sex; protected int age; public int no; }
反编译Field 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import java.lang.reflect.Field;import java.lang.reflect.Modifier;public class _665_ 反编译Field { public static void main (String[] args) throws ClassNotFoundException { StringBuilder s = new StringBuilder(); Class<?> student = Class.forName("Student" ); s.append(Modifier.toString(student.getModifiers()) + " class " + student.getName() + " {" ); Field[] declaredFields = student.getDeclaredFields(); for (Field i : declaredFields){ s.append("\n" ); s.append("\t" ); s.append(Modifier.toString(i.getModifiers())); s.append(" " ); s.append(i.getType().getSimpleName()); s.append(" " ); s.append(i.getName()); s.append(";" ); } s.append("\n" ); s.append("}" ); System.out.println(s); } }
通过反射机制访问对象属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import java.lang.reflect.Field;public class _666_ 通过反射机制访问对象属性 { public static void main (String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException { Class<?> student = Class.forName("Student2" ); Object o = student.newInstance(); Field no = student.getDeclaredField("no" ); no.set(o, 222 ); System.out.println(no.get(o)); Field name = student.getDeclaredField("name" ); name.setAccessible(true ); name.set(o, "zhangsan" ); System.out.println(name.get(o)); } } class Student2 { private String name; boolean sex; protected int age; public int no; public final double PI = 3.1415 ; }
Method 可变长度参数
可变长参数得在参数列表的最后一个
可变长参数可以当作数组来看待 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class _668_ 可变长度参数 { public static void main (String[] args) { f(); f(123 ); f(123 ,23 ,4 ,5 ,6 ); f(new int []{1 ,23 ,5 ,6 }); } public static void f (int ... args) { System.out.println("f() start~" ); for (int i : args) { System.out.println(i); } } public static void g (int n, String... args) { } }
反射Method 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 import java.lang.reflect.Method;import java.lang.reflect.Modifier;import java.lang.reflect.Parameter;public class _669_ 反射Method { public static void main (String[] args) throws ClassNotFoundException { Class<?> userService = Class.forName("UserService" ); Method[] methods = userService.getDeclaredMethods(); for (Method method : methods) { System.out.println(method); System.out.println(method.getName()); System.out.println(method.getReturnType()); System.out.println(Modifier.toString(method.getModifiers())); Parameter[] parameters = method.getParameters(); for (Parameter parameter : parameters) { System.out.println(parameter.getType().getSimpleName()); } } } } class UserService { public boolean login (String name, String password) { if (name == null ) return false ; if (name.equals(password)){ return true ; } return false ; } public void logout () { System.out.println("exit ok~" ); } }
反编译Method 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import java.lang.reflect.Method;import java.lang.reflect.Modifier;import java.lang.reflect.Parameter;public class _670_ 反编译Method { public static void main (String[] args) throws ClassNotFoundException { Class<?> userService = Class.forName("UserService" ); StringBuilder s = new StringBuilder(); s.append(userService); s.append(" {" ); Method[] declaredMethods = userService.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { s.append("\n" ); s.append("\t" ); s.append(Modifier.toString(declaredMethod.getModifiers())); s.append(" " ); s.append(declaredMethod.getReturnType()); s.append(" " ); s.append(declaredMethod.getName()); s.append("(" ); Parameter[] parameters = declaredMethod.getParameters(); for (int i = 0 ; i < parameters.length; i++ ) { s.append(parameters[i].getType().getSimpleName()); s.append(" " ); s.append(parameters[i].getName()); if (i != parameters.length - 1 ){ s.append(", " ); } } s.append(")" ); s.append("{}" ); } s.append("\n}" ); System.out.println(s); } }
反射机制调用Method 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class _671_ 反射机制调用方法 { public static void main (String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Class<?> userService = Class.forName("UserService" ); Object o = userService.newInstance(); Method login = userService.getDeclaredMethod("login" , String.class, String.class); Object invoke = login.invoke(o, "cc" , "cc" ); System.out.println(invoke); } }
Constructor 反射Constructor 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 import java.lang.invoke.CallSite;import java.lang.reflect.Constructor;import java.lang.reflect.Modifier;public class _672_ 反射Constructor { public static void main (String[] args) throws Exception { StringBuilder s = new StringBuilder(); Class<?> vip = Class.forName("Vip" ); s.append(Modifier.toString(vip.getModifiers())); s.append(" class " ); s.append(vip.getSimpleName()); s.append("{" ); Constructor<?>[] declaredConstructors = vip.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { s.append("\n\t" ); s.append(Modifier.toString(declaredConstructor.getModifiers())); s.append(" " ); s.append(declaredConstructor.getName()); s.append("(" ); Class<?>[] parameterTypes = declaredConstructor.getParameterTypes(); for (int i = 0 ; i < parameterTypes.length; i++) { s.append(parameterTypes[i].getSimpleName()); if (i != parameterTypes.length - 1 ){ s.append(", " ); } } s.append(")" ); s.append("{}" ); } s.append("\n}" ); System.out.println(s); } } class Vip { int no; String name; String birth; boolean sex; public Vip () { } public Vip (int no, String name, String birth, boolean sex) { this .no = no; this .name = name; this .birth = birth; this .sex = sex; } public Vip (int no, String name, String birth) { this .no = no; this .name = name; this .birth = birth; } public Vip (int no, String name) { this .no = no; this .name = name; } }
反射机制调用Constructor 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.lang.reflect.Constructor;public class _673_ 反射机制调用构造方法 { public static void main (String[] args) throws Exception { Class<?> vip = Class.forName("Vip" ); Object o1 = vip.newInstance(); System.out.println(o1); Constructor<?> declaredConstructor = vip.getDeclaredConstructor(int .class, String.class, String.class, boolean .class); Object o2 = declaredConstructor.newInstance(1 , "a" , "b" , true ); System.out.println(o2); } }
获取父类和父接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class _674_ 获取父类和父接口 { public static void main (String[] args) throws Exception { Class<?> stringClass = Class.forName("java.lang.String" ); Class<?> superclass = stringClass.getSuperclass(); System.out.println(superclass.getName()); Class<?>[] interfaces = stringClass.getInterfaces(); for (Class<?> anInterface : interfaces) { System.out.println(anInterface); } } }
注解 Annotation 是一种引用数据类型,编译后也是生成xxx.class文件
如何定义注解 1 2 3 4 5 6 7 8 [修饰符列表] @interface 注解类型名{ } public @interface MyAnnotation{ }
如何用在哪可以用
语法格式是:@注解类型名
注解可以出现在类上、属性上、方法上、变量上等 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class _675_ 注解怎么定义怎么用 { public static void main (String[] args) { } } @_675_ 注解怎么定义怎么用Annotationclass Annotation { @_675_ 注解怎么定义怎么用Annotation private int no; @_675_ 注解怎么定义怎么用Annotation public Annotation (int no) { this .no = no; } @_675_ 注解怎么定义怎么用Annotation public void f () { } public void g (@_675_ 注解怎么定义怎么用Annotation String name) { } } @_675_ 注解怎么定义怎么用Annotationinterface MyInterface {} @_675_ 注解怎么定义怎么用Annotationenum Season { SPRING, SUMMER, AUTUMN, WINTER }
JDK内置了哪些注解 java.lang包下的注释类型: Deprecated用@Deprecated注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。 Override表示一个方法声明打算重写超类中的另一个方法声明。 SuppressWarnings指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。
元注解 元注解是负责对其它注解进行说明的注解,自定义注解时可以使用元注解。 Java 5 定义了 4 个注解,分别是 @Documented、@Target、@Retention 和 @Inherited。Java 8 又增加了 @Repeatable 和 @Native 两个注解
@Target @Target用来标注”被标注的注解”可以出现在哪些位置
@Retention @Retention用来标注”被标注的注解”最终保存在哪 @Retention(RetentionPolicy.SOURCE):表示该注解只被保留在java源文件中。 @Retention(RetentionPolicy.CLASS):表示该注解被保存在class文件中。 @Retention(RetentionPolicy.RUNTIME):表示该注解被保存在class文件中,并且可以被反射机制锁读取
1 2 3 4 5 6 7 8 9 10 public @interface Retention{ RetentionPolicy value () ; } public enum RetentionPolcy { SOURCE, CLASS, RUNTIME }
@Deprecated 1 2 3 4 5 6 7 8 9 10 11 12 public class _678_Deprecated 注解 { @Deprecated public static void doSome () { System.out.println("doSome~" ); } public static void main (String[] args) { doSome(); } }
注解中定义属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public @interface _679_注解中定义属性Annotation { String name () ; String color () default "red" ; } public class _679_ 注解中定义属性 { @_679_ 注解中定义属性Annotation(name = "赋值" , color = "red" ) public static void doSome () { System.out.println("doSome~" ); } public static void main (String[] args) { } }
属性是value时可以省略(属性名) 如果一个注解的属性只有一个并且是value就可行
1 2 3 4 5 6 7 8 9 10 11 public class _680_ 属性是value 时可以省略 { @_680_ 属性是value时可以省略Annotation("abc" ) public static void main (String[] args) { } } public @interface _680_属性是value时可以省略Annotation { String value () ; }
属性是个数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public @interface _682_属性是个数组Annotation { int value1 () ; String value2 () ; int [] value3(); String[] value4(); Season1 value5 () ; Season1[] value6(); Class parameterType () ; Class[] parameterTypes(); } enum Season1 { SPRING, SUMMER, AUTUMN, WINTER }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public @interface _682_属性是个数组Annotation { int age () ; String[] email(); } @_682_ 属性是个数组Annotation(age = 3 , email = "xxx@qq.com" )public class _682_ 属性是个数组 { @_682_ 属性是个数组Annotation(age = 3 , email = {"xxx@qq.com" , "xxx@gmail.com" }) public static void main (String[] args) { } }
反射注解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import java.lang.annotation.ElementType;import java.lang.annotation.Target;public class _683_ 反射注解 { public static void main (String[] args) throws ClassNotFoundException { Class<?> annotation = Class.forName("_683_反射注解Annotation" ); System.out.println(annotation.isAnnotationPresent(Target.class)); if (annotation.isAnnotationPresent(Target.class)){ Target annotation1 = annotation.getAnnotation(Target.class); System.out.println(annotation1); ElementType[] value = annotation1.value(); for (ElementType elementType : value) { System.out.println(elementType); } } } }
通过反射获取注解对象属性值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import java.lang.reflect.Method;public class _684_ 通过反射获取注解对象属性值 { public static void main (String[] args) throws Exception { Class<?> c = Class.forName("MyAnnotationTest" ); Method doSome = c.getDeclaredMethod("doSome" ); if (doSome.isAnnotationPresent(_684_通过反射获取注解对象属性值Annotation.class)){ _684_通过反射获取注解对象属性值Annotation annotation = doSome.getAnnotation(_684_通过反射获取注解对象属性值Annotation.class); System.out.println(annotation.username()); System.out.println(annotation.password()); } } } class MyAnnotationTest { @_684_ 通过反射获取注解对象属性值Annotation(username = "cc" , password = "cc" ) public void doSome () { } }