java反射机制
1.反射机制的作用:
反射机制的作用:
1.反编译:
.class --> .java
2.通过反射机制访问java对象的属性、方法、构造方法等。
2.SUN公司提供的反射机制中的类:
java.lang.Class;
java.lang.reflect.Constructor;
java.lang.reflect.Filed;
java.lang.reflect.Method;
java.lang.reflect.Modifier; 修饰符
3.关于java中的可变长参数
package com.company.test;
/*
关于java中可变长参数:
*/
import java.util.Date;
import java.util.LinkedList;
public class Test02 {
// m1方法中有一个int类型可变长参数
// m1方法在调用的时候,传递的实参可以是 0-N 个。
public static void m1(int... a){
System.out.println("Test");
}
// 注意:有精确匹配的,会优先调用精确匹配的方法
public static void m1(int i){
System.out.println(i);
}
// 可变长参数可以等同看做数组
public static void m2(String... args){
for(int i=0; i<args.length; i++){
System.out.println(args[i]);
}
}
public static void m3(Class... args) throws Exception{
for(int i=0; i<args.length; i++){
Class c = args[i];
System.out.println(c.getDeclaredConstructor().newInstance());
}
}
// 可变长参数只能出现一次,并且只能出现在所有形式参数的最后一位
public static void m4(int a, String... b){}
public static void main(String[] args) throws Exception{
m1();
m1(1); // 1
m1(1,2);
m1(1,2,3,4,5);
m2("唱","跳","rap","篮球");
//实际上往m2中传入数组也是可以的
String[] a = {"唱","跳","rap","篮球"};
m2(a);
m3(Date.class, LinkedList.class);
}
}
4、获取class文件的三种方式:
package com.company.test;
import java.io.*;
import java.util.Date;
/*
获取class文件的三种方式:
*/
public class ReflectTest01{
public static void main(String[] args) throws Exception{
// 第一种方式:
Class c1 = Class.forName("Employee.class");
// 第二种方式:
// Java中每个类型都有 class 属性
Class c2 = Employee.class;
// 第三种方式:
// java.lang.Object 中有getClass()方法,所以任何一个对象都有该方法
Employee em = new Employee();
Class c3 = em.getClass(); // c3是运行时类(em的运行时类是Employee)
// 因为Employee这个类在JVM中只有一个,所以c1、c2、c3的内存地址是相同的,指向堆中唯一对象。
System.out.println(c1==c2); // true
System.out.println(c2==c3); // true
// 例子
// c4 c5 c6都代表Date这个类
Class c4 = Date.class; // c4代表 Date类,只不过这个类是SUN提供的
Class c5 = Class.forName("java.util.Date"); // 必须写类全名,类全名带有包名。
Date d = new Date();
Class c6 = d.getClass();
// c7代表int类型
Class c7 = int.class;
// 总结:上面c1 c2 c3就是表示Employee类型,c4 c5 c6表示Date类型,c7表示int类型
}
}
案例1:
package com.company.test;
public class ReflectTest02 {
public static void main(String[] args) throws Exception{
// 将A.class文件装载到JVM中的过程
//Class.forName("A"); //会执行A中的静态语句块
// 下面这句不会执行静态语句块
Class c = A.class;
}
}
class A{
static{
System.out.println("A......");
}
}
案例2:
package com.company.test;
import java.lang.Class;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
获取Class类型的对象之后,可以创建该“类”的对象
*/
public class ReflectTest03 {
public static void main(String[] args) throws Exception{
Class c = Class.forName("java.util.Date");
// 创建此Class对象所表示的类的一个新实例
Object o = c.newInstance(); // 调用Date的无参数构造方法
// Java9以后采用下面这种方法
Object o2 = c.getDeclaredConstructor().newInstance();
System.out.println(o);
System.out.println(o2);
if(o instanceof Date){
Date t = (Date)o;
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(t));
}
}
}
5、IO + Properties联合应用
1、dbinfo这样的文件,我们称之为配置文件,配置文件的作用:使程序更加灵活
注意:一般在程序中可变的东西不要写死,推荐写在配置文件中
运行同样的程序,得到不同的结果
像dbinfo这样一个具有特殊内容的配置文件,我们又叫做:属性文件
java规范中要求属性文件以“.properties”结束
属性文建中数据要求:
key和value之间可以使用“空格”、“冒号"、”等号“
如果“空格”、“冒号”、“等号”都有,按照最前面的一个符号作为分隔符
如果有中文,需要转成Unicode码
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@192.168.1.100:1521:company
username=scott
password=123456
案例:
import java.io.*;
import java.util.*;
public class Test03 {
public static void main(String[] args) throws Exception{
// 1.创建属性对象
Properties p = new Properties(); // 和Map一样,只不过key和value只能存储字符串类型
// key不能重复,如果key重复则value覆盖
// 2.创建输入流
FileInputStream fis = new FileInputStream("E:\\mydemo\\IDEA\\test\\day01\\dbinfo.properties");
// 3.将fis流中所有的数据加载到属性对象中
p.load(fis); // 所以现在属性对象中有(key=username, value=scott)
// 4.关闭流
fis.close();
// 通过key获取value
String v1 = p.getProperty("driver");
String v2 = p.getProperty("url");
String v3 = p.getProperty("username");
String v4 = p.getProperty("password");
System.out.println(v1);
System.out.println(v2);
System.out.println(v3);
System.out.println(v4);
}
}
2、反射机制 + IO + properties联合应用,动态创建java对象
可以修改配置文件中的className,不需要再重新编译程序,直接运行class文件就可以了
反射机制的作用:
能够创建灵活的代码,这些代码可以在运行时装配,无需组件之间源代码链接,降低耦合度。
反射允许我们在编写与执行时,使我们的程序代码能够装载到JVM中的内部信息,而不是
源代码中选定的类协作的代码。
注意:如果使用不当,反射成本很高。
案例2:
className=java.util.Date
package com.company.test;
import java.io.FileReader;
import java.util.Properties;
public class Test04 {
public static void main(String[] args) throws Exception{
// 1.创建属性对象
Properties p = new Properties();
// 2.创建流
FileReader fr = new FileReader("E:\\mydemo\\IDEA\\test\\day01\\classInfo.properties");
// 3.加载
p.load(fr);
// 4.关闭流
fr.close();
// 通过key获取value
String className = p.getProperty("className");
System.out.println(className);
// 通过反射机制创建对象
Class c = Class.forName(className);
// 创建对象
Object o = c.getDeclaredConstructor().newInstance();
System.out.println(o);
}
}
6、java.lang.reflect.Field; 类中的属性
package com.company.test;
/*
java.lang.reflect.Field; 类中的属性
*/
/*
public class User {
private String id;
public int age;
protected String addr;
boolean sex;
}
*/
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Test05 {
public static void main(String[] args) throws Exception{
// 获取整个类
Class c = Class.forName("User.class");
// 获取属性Field
// getField()获取单个,并且括号里面需要传入属性名
// getFields()只是获取所有public修饰的属性
Field[] fs = c.getFields();
System.out.println(fs.length); // 1
System.out.println(fs[0].getName()); // age
// 获取所有属性
Field[] fss = c.getDeclaredFields();
for(Field field:fs){
System.out.println(field.getName());
// 获取属性类型
Class type = field.getType();
// getName()和getSimpleName()区别在于返回 int和java.lang.int
System.out.println(type.getName());
System.out.println(type.getSimpleName());
// 获取属性修饰符
// int getModifiers(); 返回是int类型
// 0表示默认 1表示public 2表示private 4表示protected
int i = field.getModifiers();
System.out.println(i);
// 而java.lang.reflect.Modifier();中有一个static String toString(int mod)转换为字符串
String str = Modifier.toString(i);
System.out.println(str);
}
// 练习:将User类按照下面输出
/*
public class User {
private String id;
public int age;
protected String addr;
boolean sex;
}
*/
StringBuffer sb = new StringBuffer();
sb.append(Modifier.toString(c.getModifiers()) + "class" + c.getSimpleName() + "{" + "\n");
for(Field field:fss){
sb.append("\t");
sb.append(Modifier.toString(field.getModifiers()) + " ");
sb.append(field.getType().getSimpleName() + " ");
sb.append(field.getName() + ";" + "\n");
}
}
}
获取某个指定的类:
package com.company.test;
import java.lang.reflect.Field;
/*
java.reflect.Field;
获取某个指定的属性
*/
public class Test06 {
public static void main(String[] args) throws Exception{
//以前的方式:
/*
User u = new User();
u.id = 12; // set
System.out.println(u.age); // get
*/
Class c = Class.forName("User.class");
// 获取指定属性
Field ageF = c.getDeclaredField("age");
// 获取到某个指定属性之后可以用来? set & get
// 首先需要创建对象
Object o = c.getDeclaredConstructor().newInstance();
// 给o对象的age属性赋值12
ageF.set(o, 12);
// get
System.out.println(ageF.get(o));
// 但是如果是private String id;私有属性,能不能访问?
// 打破封装
// java.lang.reflect.Field; 继承于 java.lang.reflect.AccessibleObject;
// java.lang.reflect.AccessibleObject里面有一个方法 setAccessible
Field idF = c.getDeclaredField("id");
// 打破封装
idF.setAccessible(true); // 使用反射机制可以打破封装性,导致java对象的属性不安全
idF.set(o, "110");
System.out.println(idF.get(o));
}
}
7、java.lang.reflect.Method;类中的方法
package com.company.test;
/*
java.lang.reflect.Method;
*/
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/*
public class CustomerService{
public boolean login(String name, String pwd){
if("admin".equals(name) && "123".equals(pwd)){
return true;
}
return false;
}
public void logout(){
System.out.printlm("系统安全退出");
}
}
*/
public class Test07 {
public static void main(String[] args) throws Exception{
// 获取类
Class c = Class.forName("CustomerService");
// 获取方法
Method[] ms = c.getMethods();
for(Method m:ms){
// 方法名
System.out.println(m.getName());
// 方法返回值类型
Class returnType = m.getReturnType();
System.out.println(returnType.getSimpleName());
// 方法修饰符
System.out.println(Modifier.toString(m.getModifiers()));
// 方法形式参数列表
Class[] parameterTypes = m.getParameterTypes();
for(Class parameterType:parameterTypes){
System.out.println(parameterType.getSimpleName());
}
}
// 反编译下面代码
/*
public class CustomerService{
public boolean login(String name, String pwd){
if("admin".equals(name) && "123".equals(pwd)){
return true;
}
return false;
}
public void logout(){
System.out.printlm("系统安全退出");
}
}
*/
StringBuffer sb = new StringBuffer();
sb.append(Modifier.toString(c.getModifiers()) + " class ");
sb.append(c.getSimpleName() + "{\n");
for(Method m:ms){
sb.append("\t");
sb.append(Modifier.toString(m.getModifiers()) + " ");
sb.append(m.getReturnType().getSimpleName() + " ");
sb.append(m.getName() + "(");
// 形参
Class[] parameterTypes = m.getParameterTypes();
for(int i=0; i<parameterTypes.length; i++){
Class parameterType = parameterTypes[i];
if(i == parameterTypes.length -1){
sb.append(parameterType.getSimpleName());
}else{
sb.append(parameterType.getSimpleName() + ",");
}
}
sb.append("){}\n");
}
sb.append("}");
System.out.println(sb);
}
}
获取某个特定的方法,通过反射机制执行:
package com.company.test;
import java.lang.reflect.Method;
/*
获取某个特定的方法,通过反射机制执行
以前是:
CustomerService cs = new CustomerService();
cs.login("admin", "123");
通过反射机制呢?
*/
public class Test08 {
public static void main(String[] args) throws Exception{
// 1.获取类
Class c = Class.forName("Customer.class");
// 获取某个特定的方法
// 由于重载机制的存在,仅一个方法名并不能区分不同方法
// 通过:方法名 + 形参列表
Method m = c.getDeclaredMethod("login", String.class, String.class);
// 通过反射机制执行login方法
Object o = c.getDeclaredConstructor().newInstance();
// java.lang.reflect.Method; 中有一个方法 Object invoke(Object obj, Object... args);
// 调用o对象的m方法,传入"admin" "123"参数,方法的执行结果是returnValue
Object returnValue = m.invoke(o,"admin","123");
System.out.println(returnValue);
}
}
8、java.lang.reflect.Constructor;获取构造方法 & java.lang.reflect.Modifier;获取修饰符
package com.company.test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
/*
获取构造方法
*/
public class Test09 {
public static void main(String[] args) throws Exception{
// 1.获取类
Class c = Class.forName("java.lang.String");
// 2.获取所有的构造
Constructor[] cs = c.getDeclaredConstructors();
for(Constructor con:cs){
// 获取修饰符
System.out.println(Modifier.toString(con.getModifiers()));
// 获取构造方法名
System.out.println(con.getName());
// 或者
System.out.println(c.getSimpleName());
// 获取构造方法的形式参数列表
Class[] parameterTypes = con.getParameterTypes();
for(Class parameterType:parameterTypes){
System.out.println(parameterType.getSimpleName());
}
}
}
}
获取某个特定的构造方法,然后创建对象
package com.company.test;
import java.lang.reflect.Constructor;
/*
获取某个特定的构造方法,然后创建对象
*/
public class Test10 {
public static void main(String[] args) throws Exception{
// 1.获取类
Class c = Class.forName("Customer.class");
// 2.获取特定的构造方法
Constructor con = c.getDeclaredConstructor(String.class, int.class);
// 3.创建对象
Object o = con.newInstance("张三", 23);
System.out.println(o);
}
}
class Customer{
String name;
int age;
Customer(String name, int age){
this.name = name;
this.age = age;
}
public String toString(){
return "Customer[" + name + "," + age + "]";
}
}
9、关于类获取父类和父接口
package com.company.test;
/*
关于类获取父类和父接口
*/
public class Test11 {
// 通过反射机制获取String类的父类和父接口
public static void main(String[] args) throws Exception{
Class c = Class.forName("java.lang.String");
// 获取父类
Class superClass = c.getSuperclass();
System.out.println(superClass.getName());
// 获取父接口
Class[] ins = c.getInterfaces();
for(Class in:ins){
System.out.println(in.getName());
}
}
}