文章目录
〇、Java中万物皆对象:
Java中,我们操作的都是 对象 ,
我们如果想操作类本身,
比如:修改类的属性,修改类的方法,
由于我们 只能操作对象 ,
所以,需要把类 用一个对象来指代 ,
我们操作这个对象,
也就修改了这个类。
以此类推,
Java中万事万物都是对象,
你可以把 类 看成对象,
可以把 方法 看成对象,
可以把 属性 看成对象,
可以把 构造函数 看成对象…
我们获取了这些对象,
就可以通过操作这些对象修改它们。
一、类–>用对象表示:
1.0 获取 .class文件 的 Class 对象:
在 类加载器 将 .class文件 读取到 内存 中的时候,
jvm 会创建这个 .class文件的对象 ,
并且对每个.class文件 只创建 一个对象 存放到 jvm的 方法区 内存中,
在java.lang包下有个Class类,这个类就是.class文件的对象类型,
任何类在被使用时,都会创建这个类的Class对象。
于是拿到这个对象,在程序运行当中,我就可以访问修改某个类的属性和方法。
public class A {}//有这样一个类A:
A a1=new A();//创建了一个类A的对象a1
1.1 三种方法获取类本身:
Class c1=A.class; //直接调用A的class属性,每一个类默认都有class属性,通过调用属性获取这个类的Class对象
Class c2=a1.getClass(); //通过对象调用方法获取Class对象
Class c3=Class.forName("package1.A");
//直接告诉需要A的.class文件的Class对象,读取.class文件到内存里面,不需要.java 源文件,反编译
这三个c1、c2、c3都指向同一块内存
sout(c1==c2);//true 所以JVM将.class文件加载到内存中后,只会创建一个Class对象
1.2 获取类名:
c1.getName();//获取类名
c1.getSimpleName();//获取不包含包名的简单类名
1.3 获取类中所有方法:
Java.lang.reflect.Method类:
Method[] ms =c1.getMethods();//用数组接收,获取所有public方法,父类继承的也算
Method[] ms = c1.getDeclaredMethods();//与上面的区别是只获取自己类中的方法,包括Private的方法
//接下来遍历数组,取出每一个方法的方法名:
for(Method m:ms)
System.out.println(m.getName());//获取每一个方法的 方法名
System.out.println(m.getModifiers());
System.out.println(Modifier.toString(m.getModifiers()));//pubic\private等方法修饰符
/*接下来遍历数组,取出每一个方法的返回值:
*方法返回值也是一个类,比如int,所以,参考知识点1:
*要获取类的信息,现获取类的类类型,
*要获取返回值的名字,先要获取返回值的类*/
for(int i=0;i<ms.length;i++)
{
Class returnType =ms[i].getReturnType();//取到数组中每一个方法返回值的类类型
System.out.println(returnType.getName());//获取返回值类型
System.out.println(returnType.getSimpleName());//获取简约的返回值类型
}
/*接下来遍历数组,取出每一个方法的参数列表
*参数列表和返回值类似,只不过可能有多个参数,所以要用数组来接收*/
for(int i=0;i<ms.length;i++)
{
Class[] parameterTypes =ms[i].getParameterTypes();//取到数组中每一个方法的参数列表的类类型
for(int j=0;j< parameterTypes.length;j++)
{
System.out.println(parameterTypes[j].getName());//获取返回值类型
}
}
1.4 获取类中所有成员变量:
Java.lang.reflect.Field类:
Field[] fs=c1.getDeclaredFields();//获取自己类中的属性,包括Private的属性
Field[] fs=c1.getFields();//获取自己类中public的属性,包括继承来的
//接下来遍历数组,取出每一个属性的属性名:
for(int i=0;i<fs.length;i++)
System.out.println(fs[i].getName());
System.out.println(fs[i].getModifiers());//获取访问权限修饰符的int值,private2、public1、protected4、缺省0
Modifier.toString(fs[i].getModifiers());//帮你转换
//接下来遍历数组,取出每一个属性的类型名:
for(int i=0;i<fs.length;i++)
{
Class fieldType=field.getType();//获取成员变量的类类型
System.out.println(fieldType.getName());
System.out.println(fieldType.getSimpleName());//不带包名
}
1.5 获取类中所有构造函数:
Java.lang.reflect.Constructor类:
Constructor[] cs = c1.getDeclaredConstructors();//获取自己类中的构造函数,包括Private的构造函数
Constructor[] cs = c1.getConstructors();//获取自己类中public的构造函数,包括继承来的
//接下来遍历数组,取出每一个构造函数的名字:(其实都是类名)
for(int i=0;i<cs.length;i++)
System.out.println(cs[i].getName());
//也可采用遍历的for循环:
for(Constructor con:cs )
System.out.println(con.getName());
//接下来遍历数组,取出每一个构造函数的参数
for(int i=0;i<fs.length;i++)
{
Class[] parameterTypes = fs[i].getParameterTypes();
for(int j=0;j<parameterTypes.length;j++)
{
System.out.println(parameterTypes.getName());
}
}
1.6 获取类所在的包名:
Package pa = c1.getPackage();
System.out.println(pa.getName());
1.7 判断该类是否是一个接口:
System.out.println(c1.isInterface());
1.8 使用反射获取父类或父接口:
//获取父类:
Class sup=c.getSuperclass();
sout(sup.getName());
//获取父接口:
Class[] inter = c.getInterfaces();
for(Class i: inter){
sout(i.getName());
二、总结:修改属性的值、函数传参数调用
1.类反射:
创建对象:
Object o = c.newInstance();//调用无参构造方法
1.方法反射:
获取方法:
Method method=c.getDeclaredMethod("f",String.class,int.class);//名称,可变参数
Object o =c.newInstance();//需要一个对象
使用方法:
Object result = method.invoke(o,"admin",10); //操作的对象, 可变参数
sout(result);//打印返回值
获取方法:
Method method=c.getMethod("f",new Class[]{});//名称,参数列表的类类型
使用方法:
method.invoke(mt,new Object[]{}); //操作的对象, 参数
私有也想调用
method.setAccessible(true);
2.属性反射:
获取属性:
Field field=c.getDeclaredField("name");//属性名
Object o =c.newInstance();//需要一个对象
//给o对象上的name属性赋值为张三
field.set(o,"张三");//前传入对象,后传入需要赋的值
打印属性:
String name=(String)field.get(o);//获取o对象上name属性的值 输出 张三
修改属性:
field.set(o, name.toUpperCase());//对象,值
私有也想调用:
field.setAccessible(true);//从外部打破封装性,再进行set、get
3.构造函数反射:
获取构造方法:
Constructor constructor=c.getConstructor(new Class[]{});//参数的类类型
使用构造函数:
Student stu=(Student)constructor.newInstance(new Object[] {});//参数列表对象
4.数组的反射:
获取数组类类型:
Class c1=int[].class;
Class c2=a.getClass();
Object类:
String[][] ss= {{"aa","bb"},{"cc","dd"}};
Object oo3=ss;//把整个二维数组看作一个对象
Object[] oo4=ss;//把其中的每一个一维数组当作一个对象
Object[][] oo5=ss;//把其中的每一个字符串当作一个对象
判断是不是数组:
class c=object.getClass();
System.out.println(c.isArray());
获取数组长度
int length=Array.getLength(object);
/获取数组中的每个元素
Object obj =Array.get(object, i);//对象,第i个元素
PS、作业:使用反射反编译:
利用.class字节码,还原出类的源代码
三、方法–>用对象表示:
1.1 如何获取某一方法:
Calc类如下,有三个同名不同参的方法f:
public class Calc{
public void f(int a,int b) {
System.out.println(a+b);
}
public int f(int a,int b,int c) {
System.out.println("三个数字相加");
return a+b+c;
}
public void f() {
System.out.println("hello");
}
}
所以要获取某一方法对象,应由方法名称和参数共同决定:
public static void main(String[] args) {
Calc mt = new Calc();
Class c= mt.getClass();
try {
Method method=c.getMethod("f",new Class[]{int.class,int.class});//名称,参数列表的类类型
method.invoke(mt,new Object[]{10,10}); //操作的对象, 参数,
//等价于 mt.f(10, 10);
Method method1=c.getMethod("f",new Class[]{});//名称,参数列表的类类型
method1.invoke(mt,new Object[]{}); //操作的对象, 参数
//等价于 mt.f();
Method method2=c.getMethod("f",new Class[]{int.class,int.class,int.class});//名称,参数列表的类类型
int result=(Integer) method2.invoke(mt,new Object[]{10,10,10}); //操作的对象, 参数
//等价于 int result=mt.f(10,10,10);
}
catch(Exception e) {
e.printStackTrace();
}
}
1.2 案例1:字符串操作:
public static void main(String[] args) {
String s1="hello";
String s2="world";
System.out.println(s1.concat(s2));
System.out.println("====================");
//接下来用方法的反射操作:
try {
Class c1=s1.getClass();
Method method=c1.getMethod("concat", new Class[]{String.class});
String ss=(String) method.invoke(s1, s2);
System.out.println(ss);
} catch (Exception e) {
// TODO: handle exception
}
}
1.3 案例2:很多时候需要根据方法名称调用方法:
有一个 UserService类:
public class UserService{
public void login() {
System.out.println("login...");
}
public void update() {
System.out.println("update...");
}
public void delete() {
System.out.println("delete...");
}
}
不用方法的反射来实现:
如果要加入新的方法:比如add,
就需要修改if-else代码,较麻烦。
public static void main(String[] args) {
Scanner s= new Scanner(System.in);
String action =s.nextLine();
UserService us =new UserService();
if("login".equals(action))
us.login();
else if("update".equals(action))
us.update();
else if("delete".equals(action))
us.delete();
}
用方法的反射:
获取action字符串的内容就是方法名称,
可以直接通过方法名称获取方法对象,
然后通过方法的反射直接调用。
public static void main(String[] args) {
Scanner s= new Scanner(System.in);
String action =s.nextLine();
UserService us =new UserService();
try {
Class c=us.getClass();
Method method=c.getMethod(action, new Class[] {});
method.invoke(us,new Object[]{});
} catch (Exception e) {
}
1.4 案例3:根据标准JavaBean的属性名获取其属性值:
标准JavaBean User类:
public class User {
private String name;
private int age;
public User() {}
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
通过反射get方法:
public static void main(String[] args) {
User u =new User("zhangsan",30);
System.out.println(getValueByPropertyName("age",u));
//等价于 System.out.println(u.getAge());
}
/**
* 根据标准javaBean的属性名获取其属性值
* @param propertyName 属性的名称
* @param object 对象
* @return 属性值
*/
public static Object getValueByPropertyName(
String propertyName,Object object) {
/*
* 根据属性名,获取get方法
* 通过get方法的反射操作即可
*/
Class c=object.getClass();
String MethodName = "get"+ propertyName.substring(0,1).toUpperCase()+propertyName.substring(1);
try {
Method m =c.getMethod(MethodName,new Class[] {});
Object value = m.invoke(object, new Object[] {});
return value;
} catch (Exception e) {
return null;
}
}
四、属性–>用对象表示:
1.1 如何获取某个成员变量:
再用一次上述的User类:
public class User {
private String name;
private int age;
public User() {}
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
所以要获取某一属性对象,应由属性名决定:
public static void main(String[] args) {
User u =new User("zhangsan",30);
//获取其属性
try {
Class c=u.getClass();
//根据属性名获取成员变量
Field field=c.getDeclaredField("name");//属性名
field.setAccessible(true);//方法对象也有该函数,方法是私有的,但你也想在外部调用
//通过Field对象直接获取其属性的值
String name=(String)field.get(u);//对象
System.out.println(name);
System.out.println("=======================");
//修改其属性
field.set(u, name.toUpperCase());//对象,值
System.out.println(u.getName());
} catch (Exception e) {
}
}
1.2 案例1:修改属性:
编写函数,修改对象的属性值:
字符串全部变成大写,整型全部加100
public class FieldTest {
public static void main(String[] args) {
User u =new User("zhangsan",100);
changeValue(u);
System.out.println(u.getName()+","+u.getAge());
}
/**
* 修改对象的属性值
* 字符串属性值--->全部变成大写
* 整型属性值--->全部加100
*/
public static void changeValue(Object object) {
/*
* object这个对象有哪些属性?
* -->遍历所有属性,找出String和int,拿出属性对象,然后通过成员变量反射
*/
Class c=object.getClass();
Field[] fs=c.getDeclaredFields();//获取所有属性
for(Field field:fs)//遍历所有属性
{
//得到属性的类类型
Class fieldType=field.getType();
//判断:不建议,下面有更简单的方法
/*
if(fieldType.getSimpleName().equals("String"))
{}
if(fieldType.getSimpleName().equals("int"))
{}
*/
//判断:类类型是唯一的
try {
if(fieldType==String.class)
{
field.setAccessible(true);
String oldValue=(String)field.get(object);
field.set(object, oldValue.toUpperCase());
}
if(fieldType==int.class)
{
field.setAccessible(true);
int oldValue=(Integer)field.get(object);
field.set(object, oldValue+100);
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
1.3 案例2:获取包括属性名的字符串:
写一个方法:
public static String getSql(Object object)
该方法返回:
“insert into 类名(属性1,属性2…) value(?,?..)”
public class FieldDemo1 {
public static void main(String[] args) {
User u=new User();
System.out.println(getSql(u));
}
public static String getSql(Object obj)
{
/*
* 获取类的信息
* 类名,属性名
* 作拼接
* insert into 类名(属性1,属性2...) value(?,?...)
*/
StringBuilder s=new StringBuilder();
s.append("insert into ");
Class c=obj.getClass();
String className=c.getSimpleName();
s.append(className).append("(");
Field[] fs =c.getDeclaredFields();
for(int i=0;i<fs.length;i++) {
s=i==0?s.append(fs[i].getName()):s.append(","+fs[i].getName());
}
s.append(") values").append(getString(fs.length));
return s.toString();
}
public static String getString(int length) {
StringBuilder s=new StringBuilder();
s.append("(");
for(int i=0;i<length;i++) {
s=i==0?s.append("?"):s.append(",?");
}
return s.append(")").toString();
}
}
五、构造方法–>用对象表示:
1.1 如何获取某个构造函数:
这里用到Student类:
public class Student{
private String name;
private int age;
public Student() {}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
构造函数名是一样的、确定的,
所以要获取某一构造函数,应由参数决定:
public static void main(String[] args) {
//如何获取某一个构造函数?
//如何通过构造函数的反射创建对象?
try {
Class c=Student.class;
//获取无参数的构造方法
Constructor constructor=c.getConstructor(new Class[]{});//参数的类类型
//通过构造函数的反射,创造对象
Student stu=(Student)constructor.newInstance(new Object[] {});//参数列表对象
System.out.println("======================================================");
//获取有参数的构造方法
Constructor constructor2=c.getConstructor(new Class[]{String.class,int.class});//参数的类类型
//通过构造函数的反射,创造对象
Student stu1=(Student)constructor2.newInstance(new Object[] {"zhangsan",20});//参数列表对象
//测试
System.out.println(stu1.getAge());
System.out.println("======================================================");
System.out.println("======================================================");
//用泛型:
Constructor<Student> constructor3=c.getConstructor(new Class[]{});
Student stu2=constructor3.newInstance(new Object[] {});//不需要做强制类型转换
Constructor<Student> constructor4=c.getConstructor(new Class[]{String.class,int.class});
Student stu3=constructor4.newInstance(new Object[] {"zhangsan",20});
System.out.println(stu3.getAge());
} catch (Exception e) {
}
}
六、数组也是对象–>用对象表示:
1.1 数组类的类名是什么:
int[] a= {1,2,3};
数组a是一个对象,那么这个数组类的类类型该怎么表示呢?
我们用方法2来获取类类型,
用getName()获取类类型的名称:

得到:这个a数组的类类型为 [I
再来做三组实验:
String[] b= {"a","b","c"};
String[][] b= {{"a"},{"b","c"}};
double[] b= {1.1,2.2,3.3};



好了,以上就是这些 数组类 的 类名。
但是这样的类名我们并不能用方法1
类似:
String.class
int.class
来获取类类型:
[Ljava.lang.String.class ×
[I.class ×
1.2 怎样获取数组的类类型:
数组的类类型,只跟数组的 类型 和 维数 有关
只要类型相同,维数相同,就是同一个类
例如:对于这个数组:
int[] a= {1,2,3};
有以下两种方法获取这个数组类的类类型:
Class c1=int[].class;
Class c2=a.getClass();
证明:只要数组的 类型 和 维数 相同,就是同一个类:
System.out.println(int[].class==a.getClass());//true
String[][] ss= {{},{}};
System.out.println(String[][].class==ss.getClass());//true
1.3 案例1:方法反射——数组为参数:
public class ArrayReflectDemo1 {
public static void main(String[] args) {
try {
//B类在最下面
Class c1=B.class;
//获取B类中的test方法
Method method=c1.getMethod("test", new Class[]{String[].class,int[].class});
//使用B类中的test方法
method.invoke(c1.newInstance(), new Object[]
{new String[] {"Hello","world"},new int[] {10,20}});
} catch (Exception e) {
// TODO: handle exception
}
}
}
class B{
public void test(String[] a,int[] b) {
for(int i:b) {
System.out.println(i);
}
for(String str:a) {
System.out.println(str);
}
}
}
1.4 案例2:方法反射——混合的参数:
public class ArrayReflectDemo2 {
public static void main(String[] args) {
try {
Class c=C.class;
Method m=c.getMethod("test", new Class[]
{String[][].class,int[].class,double[].class,int.class});
m.invoke(c.newInstance(), new Object[]
{new String[][] {},new int[] {},new double[] {},10});
} catch (Exception e) {
// TODO: handle exception
}
}
}
class C{
public void test(String[][] str,int[] a,double[] c,int x) {
System.out.println("测试...");
}
}
1.5 关于Object对象:
//int数组已经是一个对象了,它不会对每一个int整型作包装工作
int[] a= {1,2,3};
Object o1=a;//OK
//Object[] o2=a;//错误
//把整个数组当作一个对象,每一个Integer当作一个对象
Integer[] b = {1,2,3,4};
Object o3=b;//把数组当作一个对象
Object[] o4=b;//把里面的每一个Integer当作对象
//字符串:
String[] s= {"hello","world"};
Object oo1=s;//把数组当作一个对象
Object[] oo2=s;//把里面的每一个字符串当作对象
String[][] ss= {{"aa","bb"},{"cc","dd"}};
Object oo3=ss;//把整个二维数组看作一个对象
Object[] oo4=ss;//把其中的每一个一维数组当作一个对象
Object[][] oo5=ss;//把其中的每一个字符串当作一个对象
1.6 案例3:Object类作为参数默认问题:
public class ArrayReflectDemo3 {
public static void main(String[] args) {
//MainTest.main(new String[] {"hello","world"});
Object[] o1=new String[] {"hello","world"};
Object oo1=new String[] {"hello","world"};
//通过main方法的反射来操作
try {
Method m=MainTest.class.getMethod("main", new Class[] {String[].class});
//m.invoke(null, new Object[] {new String[] {"hello","world"}});//静态方法不需要对象来调用
//m.invoke(null, new String[] {"hello","world"});
System.out.println("======================================================");
m.invoke(null, o1);//默认认为转换为object[]类型的数组
//这样导致成为两个字符串,而不是一个字符串数组,参数变多,报错
m.invoke(null, oo1);//正确
} catch (Exception e) {
System.out.println("...");
}
}
}
class MainTest{
public static void main (String[] args) {
for(String string:args) {
System.out.println(string);
}
}
}
1.7 案例4:判断是不是数组对象:
如果object是非数组对象,打印toString
如果object是数组对象,打印数组的每一个元素
用到 java.lang.reflect.Array 类:
public class ArrayReflectDemo4 {
//java.lang.reflect.Array;
public static void main(String[] args) {
test(new int[] {1,2,3});
test("hello");
System.out.println("===============");
printObject(new int[] {1,2,3});
System.out.println("===============");
printObject(new int[][] {{2,2,2},{1,1,1}});
}
//判断一个对象是不是数组
public static void test(Object obj)
{
Class c=obj.getClass();
System.out.println(c.isArray());
}
public static void printObject(Object object)
{
/**
* 如果object是非数组对象,打印toString
* 如果object是数组对象,打印数组的每一个元素
*/
//判断是否为数组
Class c=object.getClass();
if(c.isArray())
{
//获取数组长度
int length=Array.getLength(object);
//遍历数组的元素
for(int i=0;i<length;i++)
{
//获取数组中的每个元素
Object obj =Array.get(object, i);//对象,第i个元素
//数组中的每个元素可能又是一个数组,递归
printObject(obj);
}
}
else
{
System.out.println(object);
}
}
}
本文详细介绍了Java反射机制,包括如何获取类的Class对象、类名、方法、属性、构造函数,以及如何通过反射操作数组。通过案例展示了方法、属性和构造函数的反射应用,并探讨了数组的类类型和对象表示。文章还涵盖了如何通过反射调用方法和修改属性值,以及数组的反射操作。
1556

被折叠的 条评论
为什么被折叠?



