反射 and 注解
反射机制有什么用?
- 通过Java语言中的反射机制可以操作字节码文件。
- 优点是类似黑客。(可以读和修改字节码文件)
- 通过反射机制可以操作代码片段。(class文件)
获取类的字节码
Class.forName()
1.静态方法
2.方法的参数是一个字符串
3.字符串需要的是一个完整类名
4.完整类名必须带有包名。java.lang也不能省略
//java中任何一个对象都有一个方法:getClass()
String s = "abc";
Class x =s.getClass();//x表示String.class字节码文件,x代表String类型
//第三种方式,java语言中任何一种类型,包括基本数据类型,都有.class属性
Class z = String.class;
Class k = Date.class;
Class f = int.class;
Class e = double.class;
反射机制的好处
- 可以只需要修改properties中的配置文件,例如下面例子中的className后面的values,就可以不需要动下面的任何代码,就可以创建一个实例
public static void main(String[] args) throws Exception {
FileReader reader = new FileReader("classinfo.properties");
Properties properties = new Properties();
properties.load(reader);
reader.close();
String className = properties.getProperty("className");
//System.out.println(className);
//通过反射机制实例化对象
Class c = Class.forName(className);
Object o = c.newInstance();
System.out.println(o);
}
Class.forName()发生了什么?
-
Class.forName()这个方法的执行会导致:类加载。
重点
- 如果只希望一个类的静态代码块执行,其它代码一律不执行
- 可以使用:Class.forName(“完整类名”);
- 这个方法的执行会导致类加载,类加载时,静态代码块执行。
路径问题
//这种方式的路径缺点是:移植性差,在IDEA中默认的当前路径是project的根
//这个代码离开了IDEA就无效了
FileReader reader = new FileReader("classinfo.properties");
//通用路径
//使用这个通用方式的前提是:这个文件必须在类路径下
//凡是在src下的都是类路径下
//src是类的根路径
//获取的是绝对路径
String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
/*解释:
Thread.currentThread() 当前线程对象
getContextClassLoader() 是线程对象的方法,可以获取到当前线程的类加载器对象
getResource() [获取资源]这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源
//直接以流的形式返回
InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("");
资源绑定器
-
只能绑定xxx.properties文件。并且这个文件必须在类路径下。文件拓展名也必须是properties,""中不能加后缀properties
-
并且在写路径的时候,路径后面的拓展名不用写
-
ResourceBundle bundle = ResourceBundle.getBundle(""); String className = bundle.getString("className");
类加载器
- 启动类加载器 rt.jar
- 扩展类加载器 ext/*.jar
- 应用类加载器 classpath
双亲委派机制
- 优先从启动类加载器中加载,这个称为”父“
- ”父“无法加载到,再从扩展类加载器中加载,这个称之为”母“
- 如果都加载不到,才会考虑从应用类加载器中加载。直到加载到为止
回顾
-
什么是反射机制?反射机制有什么用?
- 操作字节码文件
- 可以让程序更加灵活
-
反射机制相关的类在哪个包下?
- java.lang.reflect.*
-
反射机制相关的主要的类?
- java.lang.Class
- java.lang.Method
- java.lang.Constructor
- java.lang.Field
-
在java中获取Class的三种方式?
-
Class c = Class.forName("完整的类名");
-
Class c = 对象.getClass();
-
Class c = int.class(); Class s = String.class();
-
-
获取了Class之后,可以调用无参数构造方法来实例化对象
-
Class c = Class.forName("完整的对象"); Object obj = c.newInstance();
-
newInstance()默认调用的是无参构造方法,如果原类中没有无参构造方法,就会报**“实例化”异常**
-
-
如果你只想让一个类的“静态代码块”执行的话,你可以怎么做?
- Class.forName(“该类的类名”);
- 这样类就加载了,类加载的时候静态代码块也跟着执行了!!
-
类路径是什么?就是项目名之前的所有东西,在IDEA中被src所指代
-
关于类路径的问题
-
//绝对路径 String path = Thread.currentThread().getContextClassLoader().getResource("xxx.yyy").getPath();
通用方式,不会收到环境移植的影响
-
//以流的形式返回 InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("xxx.yyy");
-
//以.properties结尾的文件特别使用资源绑定器 ResourceBundle bundle = ResourceBundle.getBundle("xxx"); String value = bundle.getString(key);
-
反射属性Field
-
Field翻译为字段,其实就是属性/成员
-
Filed[] fields = c.getFields();//只能获取public属性 Filed[] fields = c.getDeclaredFields();//获取所有的属性 //获取权限修饰符 int modifiers = field.getModifiers();//返回的修饰符是一个数字,每个数字是修饰符的代号! //利用toString方法来获取名称 String s = Modifier.toString(modifiers); //Class中也有getModifiers()方法
-
通过反射机制,反编译一个类的属性Field
-
Class c = Class.forName("Strudent"); Field[] fields = c.getDeclaredFields(); StringBuilder s = new StringBuilder(); s.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() + " {\n" ); for (Field field : fields ) { s.append("\t"+Modifier.toString(field.getModifiers()) + " "); s.append(field.getType().getSimpleName() + " "); s.append(field.getName()); s.append(";\n"); } s.append("}"); System.out.println(s);
怎么通过反射机制访问一个java对象的属性?
-
给属性赋值set
-
获取属性的值get
-
Class studentclass = Class.forName("Strudent"); Object object = studentclass.newInstance(); //获取balance属性(根据属性的名字来获取Field) Field balance = studentclass.getDeclaredField("balance"); //给object对象(Student对象)的balance属性赋值 /* * 虽然使用的是反射机制,但是三要素缺一不可: * 要素1:object对象 * 要素2:balance属性 * 要素3:2222值 * * 反射机制让代码复杂了,但是为了“灵活”,值得 * */ balance.set(object,2222); //读取属性的值: //两个要素:获取obj对象的no属性的值 Object o1 = balance.get(object); System.out.println(o1);
-
这种方式不能访问私有属性
-
打破封装
-
balance.setAccessible(true);
-
可变长度参数
- 语法:类型…(注意,一定是三个点)
- 只能出现在最后,只能有一个
- 可变长度参数可以当成一个数组来看待
Method***************十分重要
-
通过反射机制调用以个对象的方法?
-
Class c = Class.forName(""); Object obj = c.newInstance(); /*要素分析 要素1:对象 要素2:方法名 要素3:实参列表 要素4:返回值 */ //获取对象 Method xx = c.getDeclaredMethod("方法名",实参列表.class,....); //调用方法 Object invoke = xx.invoke(obj, 属于参数类型的值);
Constructor
- 反射机制调用构造方法
Class c= Class.forName("Strudent");
//调用无参构造方法1
Object obj = c.newInstance();
System.out.println(obj);
//调用有参数的构造方法
Constructor con = c.getDeclaredConstructor(int.class);
Object newobj = con.newInstance(22);
System.out.println(newobj);
//调用无参构造方法2
Constructor con2 = c.getDeclaredConstructor();
Object newobj2 = con2.newInstance();
System.out.println(newobj2);
注解(annotation)
-
注解Annotation是一种引用数据类型。编译之后也是生成xxx.class文件
-
怎么自定义注解?语法格式?
- [ 修饰符列表 ] @interface 注解类型名{}
-
注解怎么使用?用在什么地方?
- 注解使用的语法格式是:
- @注解类型名
- 注解可以出现在类上、属性上、方法上、变量上等
- 注解还可以出现在注解类型上
- 注解使用的语法格式是:
-
关于JDK lang包下的Override注解
- @Override这个注解只能注解方法
- @Override这个注解是给编译器参考的,和运行阶段没有关系
- 凡是java中的方法带有这个注解的,编译器都会进行编译检查,如果这个方法不是重写父类的方法,编译器报错
- 标识性注解,给编译器做参考的
-
元注解
-
什么是元注解?
- 用来标注“注解类型”的“注解”,称为元注解
- 常用的元注解:Target、Retention
-
Target
-
Target注解用来标注“被标注的注解”可以出现在哪些位置上
-
@Target(ElementType.METHOD)//表示“被标注的注解”只能出现在方法上
-
-
Retention
-
Retention注解用来标注“被标注的注解”最终保存在哪里
-
@Retention(RetentionPolicy.SOURCE)//表示该注解只被保留在java源文件中 @Retention(RetentionPolicy.CLASS)//表示该注解只被保留在class文件中 @Retention(RetentionPolicy.RUNTIME)//表示该注解只被保留在class文件中,并且可以被反射机制所读取到
-
-
-
关于JDK lang包下的Deprecated注解
- Deprecated注解标注的元素已过时
- 这个注解主要是向其它程序员传达一个信息,告知已过时,有更好的解决方案存在
-
我们通常可以在注解中定义属性
-
public @interface MyAnnotation{ String name(); //看着像一个方法,但实际上称之为一个属性 }
-
当我们使用这个注解的时候,就要给这个属性赋值
@MyAnnotation(name = "zhangsan")//如果用default设置默认值,就可以不用写了 public void doSome(){ }
-
如果一个注解属性的名字是value的话,用注解的时候,属性名可以不写
-
属性的类型可以是
- 八种基本数据类型+String,Class,枚举类型,以及他们的数组形式
-
反射注解
-
//获取这个类,方法同理 Class c = Class.forName("User"); //判断类上面是否有@MyAnnotation boolean s = c.isAnnotationPresent(MyAnnotation.class); System.out.println(s); //获取该注解对象 Annotation annotation = c.getAnnotation(MyAnnotation.class); System.out.println(annotation); //获取属性 MyAnnotation myAnnotation = (MyAnnotation) annotation; String value = myAnnotation.value(); System.out.println(value);
-
//注解的元注解一定要是Retention((RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default "我是一个中国人"; }
注解的作用
- 未来都是通过注解来开发,利用反射机制读注解
Foreach的使用
for ( 类型 名字: 传值) {
System.out.println(名字.方法);
}
可以不需要管长度,直接遍历完,一般用于数组