Java-反射机制详解

1  定义: JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,这种动态获取、调用对象方法的功能称为java语言的反射机制。

 2  作用: 反射机制可以操作字节码文件,也就是说通过反射我们可以获取构造器,对象,属性,方法(原本不知道)

一,  获取class的三种方式

    (1)   通过该类的对象去获取到对应的Class对象

Class string_class01 = " ".getClass();

    这种方法基本不用,这里显然和反射机制相悖(已有类对象 了还去用反射获取Class类对象干嘛,多此一举)

    (2) 通过全限定名来获取Class对象

Class string_class02 = Class.forName("java.lang.String");

   (3)  通过类名.class

   最好用,也更安全!

Class string_class03 = String.class;
public class test06 {

	public static void main(String[] args) throws ClassNotFoundException {
		Class string_class01 = " ".getClass();
		Class string_class02 = Class.forName("java.lang.String");
		Class string_class03 = String.class;
		System.out.println(string_class01);
		System.out.println(string_class02);
		System.out.println(string_class03);
	}

}
//结果
//class java.lang.String
//class java.lang.String
//class java.lang.String

二,类Field,Method ,Constructor

   通过反射机制反编译一个类的属性,方法,构造方法。

public class test05 {
	public static void main(String[] args) throws Exception {		
		Class user02 = Class.forName("fans.users1");
		StringBuilder str = new StringBuilder();
		str.append(Modifier.toString(user02.getModifiers())+" class "+user02.getSimpleName()+"{\n");	
		//通过Field类反射获取类users1属性
		Field[] field = user02.getDeclaredFields();
		for(Field f:field) {
			str.append("\t"+Modifier.toString(f.getModifiers())
			               +" "+f.getType().getSimpleName()
			               +" "+f.getName()+" \n");
		}	
		//通过Constructor类反射获取类users1构造方法
		Constructor[] con = user02.getConstructors();
		str.append("\n");
		for(Constructor c :con) {
			str.append("\t"+Modifier.toString(c.getModifiers())+" "
					   +user02.getSimpleName()+"(");
			Class[] pp = c.getParameterTypes();
			for(Class cc:pp) {
				str.append(cc.getSimpleName()+",");
			}
			if(pp.length>0) {
				str.deleteCharAt(str.length()-1);
			}
			str.append("){}\n");
		}			
		//通过Method类反射获取类users1方法
		Method[] method = user02.getDeclaredMethods();
		str.append("\n");
		for(Method m:method) {
			str.append("\t"+Modifier.toString(m.getModifiers())+" "
		                                 +m.getReturnType().getSimpleName()+" "
					                     +m.getName());
			str.append("( ");
			Class[] p = m.getParameterTypes();
			for(Class p01:p) {
				str.append(p01.getSimpleName()+",");
			}
			str.deleteCharAt(str.length()-1);
			str.append("){}\n");
		}
		str.append("}");
		System.out.println(str);
		
	}

}

 通过反射机制访问一个类的【属性】,调用一个类的【方法】及【构造方法】。

public class test06 {
	public static void main(String[] args) throws Exception {
        
//通过new调用users1的方法
		users1 u01= new users1();
		if( u01.login("admin", "Admin@123")) 
			System.out.println("登入成功!");
		else 
			System.out.println("用户名或密码错误!");	
		System.out.println("------------------------");
//通过反射调用users1的方法
		// 获取类的class
		Class u02class = users1.class;
		//创建对象
		Object u0201 = u02class.newInstance();//该操作会默认调用无参构造
		//获取method
		Method loginmethod = u02class.getDeclaredMethod("login", String.class,String.class);
		//获取loginmethod的invoke方法的返回值
		Object retValue = loginmethod.invoke(u0201, "admin","Admin@123");
		System.out.println(retValue);
		boolean retlogin = (boolean) retValue;
		System.out.println(retlogin?"登入成功!":"登入失败!");
		
		Method loninmethod01 = u02class.getDeclaredMethod("login", String.class);
		boolean retlogin01 = (boolean) loninmethod01.invoke(u0201, "admin");
		System.out.println(retlogin01?"登入成功!":"登入失败!");
	    
	    //调用无参方法
		u02class.getDeclaredMethod("logout").invoke(u0201);
		System.out.println("------------------------");
//通过new调用users1的构造方法	
		users1 u02 = new users1();
		users1 u03 = new users1("张三");
		users1 u04 = new users1(true, "李四");
		System.out.println("------------------------");
//通过反射调用users1的构造方法	
		//创建class
		Class u03class = Class.forName("fans.users1");
		Object u0301 = u03class.newInstance();
		
		Constructor cons = u03class.getConstructor();
		cons.newInstance();
		Constructor cons01 = u03class.getConstructor(String.class);
		cons01.newInstance("张三");
		Constructor cons02 = u03class.getConstructor(boolean.class,String.class);
		cons02.newInstance(true,"李四");
		Constructor cons03 = u03class.getConstructor(int.class,String.class);
		Object retcons03 = cons03.newInstance(22,"赖滢");
		System.out.println(retcons03);
		System.out.println("------------------------");
//通过new访问users1的属性
		users1 u05 = new users1();
		u05.name = "张三";
		u05.age = 20;
		u05.sex = true;
		System.out.println(u05);
		System.out.println("------------------------");
//通过反射访问users1的属性
		Field ff = u03class.getDeclaredField("name");
		ff.set(u0301, "张三");
		System.out.println(ff.get(u0301));
		Field ff01 = u03class.getDeclaredField("ID");
		ff01.setAccessible(true);//访问私有属性时需打破封装但会破坏其安全性
		ff01.set(u0301, 303);
		System.out.println(ff01.get(u0301));
//获取父类和父接口		
		System.out.println("------------------获取父类和父接口---------------------");
		Class strclass = String.class;
        Class strsupclass = strclass.getSuperclass();
        System.out.println(strsupclass.getName());
        
        Class[] strinter = strclass.getInterfaces();
        for(Class c : strinter) {
        	System.out.println(c.getName());
        }	
	}
}

上面案例用到users1类

public class users1 {
	private int ID;
	protected int age;
	boolean sex;
	public String name;
	public static final double MATH_PI = 3.1415926;	
    public users1() {
		super();
		System.out.println("这是无参构造!");
	}    
	public users1(String name) {
		super();
		this.name = name;
		System.out.println(this.name);
	}
	public users1(boolean sex, String name) {
		super();
		this.sex = sex;
		this.name = name;
		System.out.println(this.sex+"="+this.name);
	}
	public users1(int age, String name) {
		super();
		this.age = age;
		this.name = name;
	}
	@Override
	public String toString() {
		return "users1 [ID=" + ID + ", age=" + age + ", sex=" + sex + ", name=" + name + "]";
	}
	/**
     * 
     * @param name  账户
     * @param password 密码
     * @return
     */
    public static boolean login(String name,String password) {
    	if("admin".equals(name)&&"Admin@123".equals(password)) 
    		return true;
    	return false;
    }
    public void logout() {
    	System.out.println("账户已安全退出!");
    }
    public boolean login(String name) {
    	return "admin".equals(name)? true:false;
    }
}

三、反射API 

getModifiers(),获取字段和方法等的修饰符类型,返回值是int类型,什么都不加是0,public 是1,private是2,protected是4,static 是8,final是16,等等,多个修饰符修饰的话,返回的是每个修饰符代表的int值的和 

、、、、、、、、可自行查API文档

API文档https://www.runoob.com/manual/jdk1.6/java.base/java/lang/Class.html

四, 注释反射 

1 先创建一个名叫ID的注释

  若还不会创建和理解的请看Java-注释详解

@Documented
@Retention(RUNTIME)
@Target({ TYPE, FIELD, METHOD })
public @interface ID {
	int id() default 205;
	String name();
	mark chengji();
}

2 创建mark(代表成绩且该处用枚举实现)

  若不知如何创建或不是很理解就评论 收藏关注后期就出相应教程!

public enum mark {
	不及格,及格,良,优;
}

3 创建users类并在相应的位置标识注释

@ID(name = "zwy", chengji=mark.不及格)
public class users {
	@ID(name = "zwy", chengji = mark.优)
	private int ID;
	protected int age;
	boolean sex;
	public String name;
	public static final double MATH_PI = 3.1415926;
}

4 创建被ID注释的类没有有int型的id的异常类

 若不知如何创建或不是很理解就评论 收藏关注后期就出相应教程!

public class NotIdException extends RuntimeException{
	public NotIdException() {
		
	}
   public NotIdException(String s) {
		super(s);
	}
	
}

5 创建测试类test01并实现当被ID注释的类必须有int型的id 属性否则抛出异常!

public class test01 {
	public static void main(String[] args) throws NoSuchFieldException, SecurityException {
		Class userclass = users.class;
		Field ff = userclass.getDeclaredField("ID");
		//判断userclass的ID属性是否被注释
		System.out.println(ff.isAnnotationPresent(ID.class));
		//获取属性的注释属性值
      	ID   id	= ff.getAnnotation(ID.class);
      	System.out.println(id.id());
      	System.out.println(id.name());
      	System.out.println(id.chengji());
      	//判断userclass类是否被注释
		System.out.println(userclass.isAnnotationPresent(ID.class));
		//获取类的注释属性值
		ID idclass = (ID) userclass.getAnnotation(ID.class) ;
		System.out.println(idclass.chengji());
		//被ID注释的类必须有int型的id 属性否则抛出异常!
		boolean isid = false;
			if(userclass.isAnnotationPresent(ID.class)) {
				Field[] field = userclass.getDeclaredFields();
				for(Field f:field) {
					if("ID".equals(f.getName())&& "int".equals(f.getType().getSimpleName())) {
						isid = true;
						break;
					}	
			}
				if(!isid) {
					throw new NotIdException("被ID注释的类必须有int型的id 属性");
				}
		}
	}
}

 五, 可变长度参数

1 长度个数:0~n
2 可变 长度参数必须在参数列表最后一个位置上,而且可变长度参数只能有一个
3 可将可变长度参数看作一个数组
public class test04 {
	public static void main(String[] args) {
		m();
		m(1,2);
		m(1,2,3);
		m2(1,"abc");
		m2(1,"abc","dc");
		m3(" ","abc","de","f");
		String[] strs = {"www",".baidu.","com","//"};
		m3(strs);
		m3(new String[]{"我","是","中","国","人"});
	}
	public static void m(int... args) {
		System.out.println("m方法执行");
	}
	/*
	 * public static void m1(int... args1 ,String... args2) { }//报错!只要一个且在最后!
	 */
	public static void m2(int a,String...args) {
		System.out.println("m2方法执行");
	}
	public static void m3(String...args) {
		//args有length说明是一个数组
		for(int i=0;i

六, 扩展 

1 通过I/O流读取users1info文件来获取字符串类名进行反射操作

//通过I/O流读取userinfo文件
		FileReader reader = new FileReader("userinfo1");
		File f = new File("userinfo1");
		//创建属性类对象
		Properties p = new Properties();
		//加载
		p.load(reader);
		//关闭流
		reader.close();
		Class s3 = Class.forName(p.getProperty("students"));
		System.out.println(s3);
		

2 获取userinfo文件的绝对路径(增强代码的健壮性)

String path = Thread.currentThread().getContextClassLoader().getResource("fans/userinfo2").getPath();
InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("fans/userinfo2");

3 若获取的绝对路径有中文乱码进行解码

URLDecoder.decode(path,"UTF-8")
String path = Thread.currentThread().getContextClassLoader().getResource("fans/userinfo2").toURI().getPath();

4 使用绝对路径进行获取字符串类名

InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("fans/userinfo2");
		FileReader fr01 = new FileReader(path);
		Properties p01 = new Properties();
		p01.load(reader);
		reader.close();
		Class c01 = Class.forName(p01.getProperty("students"));
		System.out.println(c01);

5 利用Java自带的资源绑定器获取字符串类名

ResourceBundle b = ResourceBundle.getBundle("fans/userinfo2");资源绑定器
System.out.println(b.getString("students"));

6 在fans包下的txt文件userinfo2

students=fans.students

7  public private protected 的简单区别

//public private protected 的简单区别
1、public:public表明该数据成员、成员函数是对所有用户开放的,所有用户都可以直接进行调用
2、private:private表示私有,私有的意思就是除了class自己之外,任何人都不可以直接使用。
3、protected:protected对于子女、朋友来说,就是public的,可以自由使用,没有任何限制,而对于其他的外部class,protected就变成private。

Java-反射机制详解_第1张图片

8  通过反射机制访问一个Java对象的属性,并给属性赋值(set)及获取属性值(get)

public class test03 extends users {
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException  {	
		students stu01 = new students();
		users user01 = new users();
		Class usersClass = Class.forName("fans.users");
		//通过new,普通访问一个对象的属性
		//赋值
		stu01.no=111;
		//获取
		System.out.println(stu01.no);
		//使用反射机制,创建对象
		Object obj = usersClass.newInstance();//会自动调用无参构造
		//获取no属性
		Field nofield = usersClass.getDeclaredField("no");
		//给obj对象no属性赋值
		nofield.set(obj, 222);
		System.out.println(nofield.getName()+"="+nofield.get(obj));
		//访问私有属性需要打破封装(打破封装会带来安全问题)才能访问赋值获取
		Field idfield = usersClass.getDeclaredField("ID");
		idfield.setAccessible(true);//打破封装
		idfield.set(obj, 306);
		System.out.println(idfield.getName()+"="+idfield.get(obj));
		
		Field agefisld = usersClass.getDeclaredField("age");
		agefisld.set(obj, 18);
		System.out.println(agefisld.getName()+"="+ agefisld.get(obj));
		
		//正常new的对象不能访问私有属性,要访问须继承对象类并获取set(),get()方法
		//user01.ID;//报错访问不了
		//去users类下获取获取set(),get()方法,通过调用方法进行赋值和取值
		user01.setID(207);
		System.out.println("user01ID="+ user01.getID());
			
	}
}

9   只要类加载静态代码快就会执行(所以若只执行类中的静态代码快可以使用反射机制进   行加载)

public class test02 {
	public static void main(String[] args) throws ClassNotFoundException, IOException, URISyntaxException {
		//students s = new students();
        Class c = Class.forName("fans.students");
	}
}
class students {
	int no;
	private int ID;
	public students() {
		System.out.println("这是个无参构造方法!");
	}
    static {
    	System.out.println("这里是静态代码块");
    }	
}
 

//该通过new对象进行实例化加载会运行整代码块
//运行结果:

Java-反射机制详解_第2张图片

//通过反射进行类加载只会运行静态类加载块 

 10  获取Class之后,可以调用无参数构造方法来实例化对象

//通过反射获取class ,通过class实例化对象
		Class s = Class.forName("fans.students");
		Object s1  = s.newInstance();//已过时,该方法会默认调用无参构造方法,所以必须保证无参构造存在。
		//通过new实例化对象
		students s2 = new students();

 11 Java的getCanonicalName和getName ,getSimpleName()

Java的getCanonicalName和getName ,getSimpleName()https://blog.csdn.net/hustzw07/article/details/71108945

你可能感兴趣的:(Java,java,eclipse,intellij-idea)