概述
反射机制有什么用
通过反射机制可以操作字符码文件(就是编译后的那个class文件),在java.lang.reflect包下。
相关的类:
java.lang.class :代表字节码文件
java.lang.reflect.Method:代表字节码中的方法字节码
java.lang.reflect.Constructor:代表字节码中的构造方法字节码
java.lang.reflect.Field:代表字节码中的属性字节码
获取字节码文件的三种方式
package a.b;
public class Test01 {
public static void main(String[] args) {
/*
第一种拿到字节码文件的方式
Class.forName()
1,静态方法
2,方法的参数是一个字符串
3,字符串需要是一个完整的类名,包括包的名
*/
Class c1=null;
try {
c1=Class.forName("java.lang.String");//c1代表String.class文件
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
/*
第二种拿到字节码文件的方式,java中任何一个对象都有一个方法:getClass()
通过引用点方式获取相应的类
*/
String s="abc";
Class x=s.getClass();//x代表String.class字节码文件,x代表String类型
System.out.println(c1==x);//结果为true,双等号是判断的内存地址,c1和x指向的是同一份字节码文件
/*
第三种拿到字节码文件的方式,java中任何一种类型,包括基本类型,它都有class属性
*/
Class z=String.class;//z代表String类型
System.out.println(x==z);
}
}
通过反射实例化对象
newInstance();方法调用User类的无参构造方法,完成User对象的创建,必须保证无参构造方法存在
package a.b;
public class Test02 {
public static void main(String[] args) {
try {
//通过反射机制实例化对象
Class c=Class.forName("a.b.User");//右键User类名,点Copy Reference就可以复制带着包名了
Object o= null;
try {
o = c.newInstance();//调用User类的无参构造方法,完成User对象的创建,必须保证无参构造方法存在
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
System.out.println(o);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
反射这种方式创建对象更加灵活
package a.b;
import java.io.FileReader;
import java.util.Properties;
public class Test03 {
public static void main(String[] args)throws Exception {
//通过IO流读取属性配置文件
FileReader reader=new FileReader("FanSheJiZhi/classinfo.properties");
//创建属性类对象Map
Properties pro=new Properties();
//加载
pro.load(reader);//属性配置文件就加载到属性集合上了
//关闭流
reader.close();
///通过key获取value
String s=pro.getProperty("classname");
System.out.println(s);
//通过反射机制实例化对象
Class c= Class.forName(s);
Object o=c.newInstance();
System.out.println(o);
}
}
不改变源代码的基础之上,可以做到不同对象的实例化
符合OCP开闭原则,对扩展开放,对修改关闭
forname执行静态代码块
如果只是想让一个类的静态代码块执行的话,其他代码不执行,可以Class.forname
package a.b;
public class User {
static{
System.out.println("执行了");
}
}
package a.b;
public class Test {
public static void main(String[] args) {
try {
Class.forName("a.b.User");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
关于路径问题
以上方式填写的路径,移植性差,在IDEA中默认的当前路径是project的根,如果离开了IDEA,可能当前路径不是project的根了,这个路径就无效了
以下较为通用
(使用前提是这个文件必须在类路径下,凡是在src下的都是类路径,src是类的根路径)
/*
Thread.currentThread()当前线程对象
getContextClassLoader()线程方法,获取当前线程的类加载器对象
getResource类加载器的方法,类加载器默认的根路径下加载资源,起点是从类的根路径下开始加载
*/
String path=Thread.currentThread().getContextClassLoader().getResource("文件名").getPath();
System.out.println(path);
这种方式可以不止windows系统
String path=Thread.currentThread().getContextClassLoader().getResource("a/b/db.properties").getPath();
//该文件在src下的a包下的b包下
package a.b;
import java.io.FileReader;
import java.util.Properties;
public class Test04 {
public static void main(String[] args) throws Exception{
String path=Thread.currentThread().getContextClassLoader().getResource("a/b/db.properties").getPath();
FileReader reader=new FileReader(path);
Properties pro=new Properties();
pro.load(reader);
reader.close();
pro.getProperty("classname");
System.out.println(pro.getProperty("classname"));
}
}
直接以流的方式返回
上述方式是先获取个路径,然后new个FileReader流对象,将这个流对象传到pro.load参数中,将流加载到properties集合中
下面这个方式是直接返回流,不用在获取路径了
import java.io.InputStream;
import java.util.Properties;
public class Test04 {
public static void main(String[] args) throws Exception{
// String path=Thread.currentThread().getContextClassLoader().getResource("a/b/db.properties").getPath();
// FileReader reader=new FileReader(path);
InputStream in=Thread.currentThread().getContextClassLoader().getResourceAsStream("a/b/db.properties");
Properties pro=new Properties();
pro.load(in);
in.close();
pro.getProperty("classname");
System.out.println(pro.getProperty("classname"));
}
}
资源绑定器
java.util包下有个资源绑定器,便于获取配置文件下的内容
必须是属性配置文件
属性配置文件xxx.properties必须在类路径下
并且写路径的时候,路径后面的扩展名不能写
package a.b;
import java.util.ResourceBundle;
public class ResourceBundleTest {
public static void main(String[] args) {
//getBundle("a/b/db")自动将这个路径下的文件加载到propert集合中了
ResourceBundle bundle=ResourceBundle.getBundle("a/b/db");
String classname=bundle.getString("classname");
System.out.println(classname);
}
}
类加载机制
启动类加载器
扩展类加载器
应用类加载器
双亲委派机制,一种安全机制,你自己写的一个String类,jvm加载的时候先是“启动类加载”,不会加载你写的,而是SUN公司写的
启动类加载器被称为“父”,扩展类加载器被称为“母”
获取Field(属性)
反射Field
package a.b;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class FieldTest1 {
public static void main(String[] args) throws Exception {
//获取整个类
Class c=Class.forName("a.b.Student");
//获取所有的Field
//getFields()获取所有的公开的属性
Field[]fields= c.getFields();
// System.out.println(fields.length);//结果为1
// System.out.println(fields[0]);
//getDeclaredFields()获得所有属性
Field[]fieldss=c.getDeclaredFields();
//System.out.println(fieldss.length);
for (int i = 0; i <fieldss.length ; i++) {
//获得所有属性的名字
System.out.println(fieldss[i].getName());
//获得属性的类型
//System.out.println(fieldss[i].getType());//getType()返回的是个class
//System.out.println(fieldss[i].getType().getName());//全名
System.out.println(fieldss[i].getType().getSimpleName());
//获得属性的修饰词
//getModifiers()返回的是int型,Modifier类有个静态方法可以将代号转化成字符串
//System.out.println(fieldss[i].getModifiers());
int i1=fieldss[i].getModifiers();
Modifier.toString(i1);
System.out.println(Modifier.toString(i1));
}
}
}
class Student{
//Field为字段就是属性
//分别采用了不同的修饰符
public int no;
private String name;
protected int age;
boolean sex;
}
反编译Field
给一个class文件,可以拿到java源码
package a.b;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class FanBanyi {
public static void main(String[] args) throws Exception{
//字符串拼接
StringBuilder s=new StringBuilder();
//获取类
Class c=Class.forName("java.lang.Integer");
s.append(Modifier.toString(c.getModifiers())+" class "+c.getSimpleName()+"{"+"\n");
for (Field field:c.getDeclaredFields()
) {
s.append("\t");
s.append(Modifier.toString(field.getModifiers()));
s.append(" ");
s.append(field.getType().getSimpleName());
s.append(" ");
s.append(field.getName());
s.append(";\n");
}
s.append("}");
System.out.println(s);
}
}
通过反射机制访问对象的属性
给属性复赋值
获取属性的值
package a.b;
import java.lang.reflect.Field;
public class Test05 {
public static void main(String[] args) throws Exception{
Class c= Class.forName("a.b.Student");
Object o=c.newInstance();
//获取属性(根据属性的名称获得属性)
Field field= c.getDeclaredField("no");
//给o对象(student对象)的no属性赋值
field.set(o,222);//给o对象的no属性赋值222
//读取属性的值
field.get(o);
//私有修饰的属性通过上述方式修改不了
//打破封装field1.setAccessible(true);反射机制的缺点
Field field1=c.getDeclaredField("name");
field1.setAccessible(true);
field1.set(o,"jack");
System.out.println(field1.get(o));
}
}
有个叫打破封装的,反射机制的缺点
获取Method
可变长参数
package a.b;
/*
可变长度参数
类型...
int ...args是可变长都参数
1.可变长度是0到n个
2.可变长参数必须在列表中的最后一个
3.可以当一个数组来处理,这里的args就是一个数字的数组,也可以传一个数组进去
*/
public class ArgsTest {
public static void main(String[] args) {
m(1,2);
m(2);
}
public static void m(int...args){
System.out.println("执行了");
}
}
反射Method
package a.b;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Test06 {
public static void main(String[] args) throws Exception{
//获取类
Class c= Class.forName("a.b.UserService");
//获取Method,所有的包括私有的
Method [] methods=c.getDeclaredMethods();
for (Method method:methods
) {
//获取方法名
System.out.println(method.getName());
//获取方法返回值类型
method.getReturnType().getSimpleName();
System.out.println(method.getReturnType().getSimpleName());
//获取修饰符
int i=method.getModifiers();
Modifier.toString(i);
System.out.println(Modifier.toString(i));
//获取方法的参数列表,由两部分,一是参数类型,二是参数变量名
Class [] classes= method.getParameterTypes();
for (Class clas:classes){
System.out.println(clas.getSimpleName());
}
}
}
}
class UserService{
public boolean login(String name,String password){
if("admin".equals(name)&&"123".equals(password)){
return true;
}
return false;
}
public void logout(){
System.out.println("系统退出成功");
}
}
反编译Method
package a.b;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class FanBianyiFangfa {
public static void main(String[] args) throws Exception{
StringBuilder s=new StringBuilder();
Class c=Class.forName("java.lang.String");
s.append(Modifier.toString(c.getModifiers())+" class "+c.getSimpleName()+"{\n");
Method []methods=c.getDeclaredMethods();
//public boolean login(String name,String password)
for (Method method:methods
) {
s.append("\t");
s.append( Modifier.toString(method.getModifiers()));
s.append(" ");
s.append(method.getReturnType().getSimpleName());
s.append(" ");
s.append(method.getName());
s.append("(");
//参数列表
Class []classes=method.getParameterTypes();
for (Class cp:classes
) {
s.append(cp.getSimpleName());
s.append(",");
}
s.deleteCharAt(s.length()-1);
s.append("){}\n");
}
s.append("}");
System.out.println(s);
}
}
通过反射机制调用一个对象的方法
package a.b;
public class UserService1 {
public boolean login(String name,String password){
if("admin".equals(name)&&"123".equals(password)){
return true;
}
return false;
}
public void logout(){
System.out.println("系统退出成功");
}
}
import java.lang.reflect.Method;
public class Test07 {
public static void main(String[] args) throws Exception{
Class c=Class.forName("a.b.UserService");
Object obj=c.newInstance();
//获取方法,因为有重载,所以不能只要方法名
Method method=c.getDeclaredMethod("login", String.class, String.class);
//调用方法,要素1对象要素2方法名要素3参数列表要素4返回值
//invoke调用的意思
Object reValue= method.invoke(obj,"admin","123");
System.out.println(reValue);
}
}
有个小插曲,如果UserService这个类还是像之前在Test06下的时候,调用会出错
can not access a member of class a.b.UserService with modifiers
获取Constructor
反射Constructor
反编译Constructor
package a.b;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
public class Test07 {
public static void main(String[] args) throws Exception{
StringBuilder s=new StringBuilder();
Class c=Class.forName("a.b.Vips");
s.append(Modifier.toString(c.getModifiers())+" class "+c.getSimpleName()+"{\n");
Constructor []constructors=c.getConstructors();
for (Constructor cs:constructors
) {
s.append("\t");
s.append(Modifier.toString(cs.getModifiers())+" "+c.getSimpleName());//构造方法的方法名就是类名
s.append("(");
Class []classes=cs.getParameterTypes();
for (Class css:classes
) {
s.append(css.getSimpleName());
s.append(",");
}
//删除最后下标位置上的字符
//s.deleteCharAt(s.length()-1);
if(classes.length>0){
s.deleteCharAt(s.length()-1);
}
s.append(")");
s.append("\n");
}
s.append("}");
System.out.println(s);
}
}
class Vips{
int no;
String name;
String birth;
boolean sex;
public Vips() {
}
public Vips(int no, String name, String birth, boolean sex) {
this.no = no;
this.name = name;
this.birth = birth;
this.sex = sex;
}
}
反射机制调用构造方法(new对象)
package a.b;
import java.lang.reflect.Constructor;
public class Test08 {
public static void main(String[] args)throws Exception {
//通过反射调用构造方法
Class c=Class.forName("a.b.Vips");
//无参构造
Object o1=c.newInstance();
//有参构造
//第一步,获取到这个有参数的构造方法
Constructor constructor=c.getDeclaredConstructor(int.class,String.class,String.class,boolean.class);
//调用这个方法new对象
Object o2=constructor.newInstance(110,"jack","1990-04-21",true);
}
}
获取父类和实现的接口
package a.b;
public class Test09 {
public static void main(String[] args)throws Exception {
Class c=Class.forName("java.lang.String");
//获取父类
String name =c.getSuperclass().getName();
System.out.println(name);
//获取实现的接口
Class []classes=c.getInterfaces();
for (Class is:classes
) {
System.out.println(is.getSimpleName());
}
}
}
注解
Annotation
是一种引用数据类型,编译之后也生成class文件
语法格式
[修饰符列表]@interface注解类型名{ }
怎么使用?
@加上类型名
给编译器做参考的,编译阶段起作用
三个jdk自带的Annotation
@Override: 限定重写父类方法, 该注释只能用于方法
@Deprecated: 用于表示某个程序元素(类, 方法等)已过时
@SuppressWarnings: 抑制编译器警告.
元注解
注解的注解叫元注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
常见的元注解Target,Retention
Target用来标注”注解类型“的”注解“,用来标注”被标注的注解“出现在哪个位置上,比如表示这个注解只可以出现在.METHOD(方法)上
Retention用来表示标注的注解存在什么地方,
.SOURCE表示只存在java源文件中
.CLASS表示保存在class文件中
.RUNTIME表示保存在class文件中,并且被反射机制读取到
注解应用
需求:
有一个注解@Id,只能出现在类上,凡是类上面出现@Id注解,要求该类必须有一个int型的id属性,如果没有就异常
自定义注解
package a.c;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//表示这个注解只能出现在类上
@Target(ElementType.TYPE)
//该注解可以被反射机制读取到
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {
}
自定义异常
package a.c;
/*
自定义异常
*/
public class HasnotIDException extends RuntimeException{
public HasnotIDException() {
}
public HasnotIDException(String message) {
super(message);
}
}
自定义类,有@Id注解
package a.c;
@Id
public class User {
int id;
}
基于注解方式编程
package a.c;
import java.lang.reflect.Field;
public class Zhujie {
public static void main(String[] args)throws Exception {
Class c1=Class.forName("a.c.User");
//判断类是否有注解
if(c1.isAnnotationPresent(Id.class)){
//如果有注解在进行判断是否类符合注解要求
Field [] fields=c1.getDeclaredFields();
boolean isRight=false;//给一个默认标记
for (Field field:fields
) {
//每次循环,拿出一个属性做判断
if("id".equals(field.getName())&&"int".equals(field.getType().getSimpleName())){
//表示有个int类型的id属性
isRight=true;//表示合法,就不用循环了
break;
}
}
if(!isRight){
//判断是否合法,这如果不合法,则抛异常
throw new HasnotIDException("该类违反了注解Id的规定");
}
}
}
}