在开始今天的学习前先补充一点内容,如何在eclipse快速输出System.out.println(),
输入syso然后ALT+/ 就可以了
或者首先输入sysout,然后ALT+/
反射基本理解
反射:Reflection,它是一种在程序运行时可以将其状态作为数据进行查询与修改的能力例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
反射提供的能力:
- 有类名,可以获取其自身信息构造方法,方法和属性(域)信息创建实例
- 有引用,可以:根据方法名调用其方法设置其属性值动态创建数值,并管理其元素
Class类详解
反射的核心该类来自java.lang包,泛型类在运行时可以使用它发现类的所有信息一个jvm中每一个加载的类只会有一个Class对象
package com.yuyi.Refletion;
class User{ //创建一个User类,里面有一个静态方法输出
static {
System.out.println("User init...");
}
}
public class reflectionDemo {
static void create1(){ //创建一个方法create1,里面实例化User()
new User();
}
static void create2() { //创建create2方法,里面创建一个泛型类c等于User的反射
Class<User> c=User.class;
}
static void create3(){ //创建create3方法,里面同样是是实例化c等于User的反射
try {
Class c=Class.forName("com.yuyi.Refletion.User");
}catch(ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[]args) {
create1();
Class<String> c=String.class; //实例化c等于String的反射
String s=""; //创建字符串s为空
Class<?> c1=s.getClass(); //泛型c1等于s的反射
try
{
Class<?> c2= Class.forName("java.lang.String"); //泛型c2等于java.lang.String的反射
}catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
获取class的三种方式(摘自CSDN敬业的小码哥)链接:
1 package com.yuyi.Refletion;
2
3
4 /**
5 * 獲取Class對象的三種方式
6 * @author Mike
7 *1.Object--getclass();
8 *2. 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
9 *3. 通过Class类的静态方法:forName(String className)(常用)
10
11 */
12
13
14 public class Fanshe {
15
16 public static void main(String[] args) {
17 // TODO Auto-generated method stub
18 //第一种方式获取Class对象
19 Student stu1=new Student();//這一new產生一個Student對象,一個Class對象
20 Class stuclass=stu1.getClass();//獲取Class對象
21 System.out.println(stuclass.getName());
22
23 //第二種方式獲取Class對象
24 Class stuclass2=Student.class;
25 System.out.println(stuclass==stuclass2);//判斷第一種方式獲取的class對象和第二種方式獲取的是否相同
26
27 //第三種方式獲取Class對象
28 try {
29 Class stuclass3=Class.forName("com.yuyi.Refletion.Student");//注意此字符串必須是真實路徑
30 System.out.println(stuclass3==stuclass2);//判斷第三種方式是不是同一個class
31
32 } catch (ClassNotFoundException e) {
33 e.printStackTrace();
34 // TODO: handle exception
35 }
36 }
37
38 }
通过反射获取构造方法并使用
Student类
package com.yuyi.Refletion;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class Student {
//-------------构造方法-----------
//默认的构造方法
Student(String str){
System.out.println("(默认)的构造方法s="+str);
}
//无参构造方法
public Student() {
System.out.println("调用了公有,无参构造方法执行。。。");
}
//有一个参数的构造方法
public Student(char name) {
System.out.println("姓名"+name);
}
//有多个参数的构造方法
public Student(String name,int age) {
System.out.println("姓名"+name+"年龄"+age);
}
//受保护的构造方法
protected Student (boolean n) {
System.out.println("受保护的构造方法n="+n);
}
//私有构造方法
private Student(int age) {
System.out.println("私有的构造方法 年龄:"+age);
final int i=0;
}
public static void main(String[] main) throws ClassNotFoundException {
try {
Class c1=Class.forName("com.yuyi.Refletion.Student"); //创建反射c1,获取Student类
System.out.println(c1.getName()); //输出c1所属的包,类方法
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Class c2=Class.forName("com.yuyi.Refletion.Student"); //获取所有构造方法
Constructor[] conArray=c2.getConstructors();
for(int i=0;i<conArray.length;i++) {
Class[] parameterTypes=conArray[i].getParameterTypes();
System.out.println("第"+i+"个构造函数");
for(int j=0;j<parameterTypes.length;j++) {
System.out.println(parameterTypes[j].getName()+",");
}
}
Class<Student> c=Student.class;
try {
Field f1=c.getField(null);
System.out.println(f1.getName());
} catch (NoSuchFieldException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package com.yuyi.Refletion;
import java.lang.reflect.Constructor;
/*
* 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
*
* 1.获取构造方法:
* 1).批量的方法:
* public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
* 2).获取单个的方法,并调用:
* public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
* public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
*
* 调用构造方法:
* Constructor-->newInstance(Object... initargs)
*/
public class Constructors {
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
//1.加載class對象
Class clazz=Class.forName("com.yuyi.Refletion.Student");
//2.獲取共有構造方法
System.out.println("****************所有共有構造方法******************");
Constructor[] conArray=clazz.getConstructors();
for(Constructor c:conArray) {
System.out.println(c);
}
System.out.println("****************所有共有構造方法(包括:私有,受保護,默認,公有)******************");
conArray=clazz.getDeclaredConstructors();
for(Constructor c:conArray) {
System.out.println(c);
}
System.out.println("***************獲取公有,無參的構造方法*****************");
Constructor con=clazz.getConstructor(null);
//1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
//2>、返回的是描述这个无参构造函数的类对象。
System.out.println("con="+con);
//調用構造方法
Object obj=con.newInstance();
// System.out.println("obj = " + obj);
// Student stu = (Student)obj;
System.out.print("***********獲取私有構造方法,并調用*************");
con=clazz .getDeclaredConstructor(char.class);
System.out.println(con);
//調用構造方法
con.setAccessible(true);
obj=con.newInstance('男');
}
}
调用方法:
1.获取构造方法:
1).批量的方法:public Constructor[] getConstructors():所有"公有的"构造方法 public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有) 2).获取单个的方法,并调用:public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有; 调用构造方法:
Constructor-->newInstance(Object... initargs)
newInstance是 Constructor类的方法(管理构造函数的类)
api的解释为:
newInstance(Object... initargs) 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象。并为之调用
3、获取成员变量并调用
student类:
1 ackage fanshe.field;
2
3 public class Student {
4 public Student(){
5
6 }
7 //**********字段*************//
8 public String name;
9 protected int age;
10 char sex;
11 private String phoneNum;
12
13 @Override
14 public String toString() {
15 return "Student [name=" + name + ", age=" + age + ", sex=" + sex
16 + ", phoneNum=" + phoneNum + "]";
17 }
18
19
20 }
测试类:
package fanshe.field;
import java.lang.reflect.Field;
/*
* 获取成员变量并调用:
*
* 1.批量的
* 1).Field[] getFields():获取所有的"公有字段"
* 2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
* 2.获取单个的:
* 1).public Field getField(String fieldName):获取某个"公有的"字段;
* 2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
*
* 设置字段的值:
* Field --> public void set(Object obj,Object value):
* 参数说明:
* 1.obj:要设置的字段所在的对象;
* 2.value:要为字段设置的值;
*
*/
public class Fields {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class stuClass = Class.forName("fanshe.field.Student");
//2.获取字段
System.out.println("************获取所有公有的字段********************");
Field[] fieldArray = stuClass.getFields();
for(Field f : fieldArray){
System.out.println(f);
}
System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
fieldArray = stuClass.getDeclaredFields();
for(Field f : fieldArray){
System.out.println(f);
}
System.out.println("*************获取公有字段**并调用***********************************");
Field f = stuClass.getField("name");
System.out.println(f);
//获取一个对象
Object obj = stuClass.getConstructor().newInstance();//产生Student对象--》Student stu = new Student();
//为字段设置值
f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华"
//验证
Student stu = (Student)obj;
System.out.println("验证姓名:" + stu.name);
System.out.println("**************获取私有字段****并调用********************************");
f = stuClass.getDeclaredField("phoneNum");
System.out.println(f);
f.setAccessible(true);//暴力反射,解除私有限定
f.set(obj, "18888889999");
System.out.println("验证电话:" + stu);
}
}
获取成员方法并调用
student类:
package fanshe.method;
public class Student {
//**************成员方法***************//
public void show1(String s){
System.out.println("调用了:公有的,String参数的show1(): s = " + s);
}
protected void show2(){
System.out.println("调用了:受保护的,无参的show2()");
}
void show3(){
System.out.println("调用了:默认的,无参的show3()");
}
private String show4(int age){
System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
return "abcd";
}
}
测试类:
package fanshe.method;
import java.lang.reflect.Method;
/*
* 获取成员方法并调用:
*
* 1.批量的:
* public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
* public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
* 2.获取单个的:
* public Method getMethod(String name,Class<?>... parameterTypes):
* 参数:
* name : 方法名;
* Class ... : 形参的Class类型对象
* public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
*
* 调用方法:
* Method --> public Object invoke(Object obj,Object... args):
* 参数说明:
* obj : 要调用方法的对象;
* args:调用方式时所传递的实参;
):
*/
public class MethodClass {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class stuClass = Class.forName("fanshe.method.Student");
//2.获取所有公有方法
System.out.println("***************获取所有的”公有“方法*******************");
stuClass.getMethods();
Method[] methodArray = stuClass.getMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取所有的方法,包括私有的*******************");
methodArray = stuClass.getDeclaredMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取公有的show1()方法*******************");
Method m = stuClass.getMethod("show1", String.class);
System.out.println(m);
//实例化一个Student对象
Object obj = stuClass.getConstructor().newInstance();
m.invoke(obj, "刘德华");
System.out.println("***************获取私有的show4()方法******************");
m = stuClass.getDeclaredMethod("show4", int.class);
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println("返回值:" + result);
}
}
反射main方法
student类:
package fanshe.main;
public class Student {
public static void main(String[] args) {
System.out.println("main方法执行了。。。");
}
}
测试类:
package fanshe.main;
import java.lang.reflect.Method;
/**
* 获取Student类的main方法、不要与当前的main方法搞混了
*/
public class Main {
public static void main(String[] args) {
try {
//1、获取Student对象的字节码
Class clazz = Class.forName("fanshe.main.Student");
//2、获取main方法
Method methodMain = clazz.getMethod("main", String[].class);//第一个参数:方法名称,第二个参数:方法形参的类型,
//3、调用main方法
// methodMain.invoke(null, new String[]{"a","b","c"});
//第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
//这里拆的时候将 new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一
// methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二
} catch (Exception e) {
e.printStackTrace();
}
}
}
6、反射方法的其它使用之---通过反射运行配置文件内容
student类:
public class Student {
public void show(){
System.out.println("is show()");
}
}
配置文件以txt文件为例子(pro.txt):
className = cn.fanshe.Student
methodName = show
测试类:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;
/*
* 我们利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改
* 我们只需要将新类发送给客户端,并修改配置文件即可
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过反射获取Class对象
Class stuClass = Class.forName(getValue("className"));//"cn.fanshe.Student"
//2获取show()方法
Method m = stuClass.getMethod(getValue("methodName"));//show
//3.调用show()方法
m.invoke(stuClass.getConstructor().newInstance());
}
//此方法接收一个key,在配置文件中获取相应的value
public static String getValue(String key) throws IOException{
Properties pro = new Properties();//获取配置文件的对象
FileReader in = new FileReader("pro.txt");//获取输入流
pro.load(in);//将流加载到配置文件对象中
in.close();
return pro.getProperty(key);//返回根据key获取的value值
}
}
控制台输出:
is show()
需求: 当我们升级这个系统时,不要Student类,而需要新写一个Student2的类时,这时只需要更改pro.txt的文件内容就可以了。代码就一点不用改动
要替换的student2类:
public class Student2 {
public void show2(){
System.out.println("is show2()");
}
}
配置文件更改为:
className = cn.fanshe.Student2
methodName = show2
控制台输出:
is show2();
7、反射方法的其它使用之---通过反射越过泛型检查
泛型用在编译期,编译过后泛型擦除(消失掉)。所以是可以通过反射越过泛型检查的
测试类:
import java.lang.reflect.Method;
import java.util.ArrayList;
/*
* 通过反射越过泛型检查
*
* 例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值?
*/
public class Demo {
public static void main(String[] args) throws Exception{
ArrayList<String> strList = new ArrayList<>();
strList.add("aaa");
strList.add("bbb");
// strList.add(100);
//获取ArrayList的Class对象,反向的调用add()方法,添加数据
Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
//获取add()方法
Method m = listClass.getMethod("add", Object.class);
//调用add()方法
m.invoke(strList, 100);
//遍历集合
for(Object obj : strList){
System.out.println(obj);
}
}
控制台输出:
aaa bbb 100