java - 反射 - 类,属性

class描述了一类事物(对象)

反射则是用来描述class

可以理解为class进行操作

System.out.print下面的注解一般是输出结果

package reflect;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

public class ReflectTest {
    public static void main(String[] args){

        try {
            System.out.println("-------------------------------------获取类--------------------------------------------------");
            //直接获取一种class:
            //方法一:包名.类名: package.class
            Class clazz1 = Class.forName("reflect.TestClass");
            //方法二:直接通过类
            Class clazz2 = TestClass.class;
            //方法三:通过对象
            TestClass tc1 = new TestClass();
            Class clazz3 = tc1.getClass();

            System.out.println("-------------------------------------获取类的修饰符--------------------------------------------------");
            //获取class的修饰符  //这个获取很容易不过很难看懂,所以写个方法modifierHelper用来解析
            int modify = clazz1.getModifiers();
            System.out.println(modify);
            System.out.println(modifierHelper(modify));
            //1
            //public
            modify = Runnable.class.getModifiers(); //换个java自带接口:Runnable
            System.out.println(modifierHelper(modify));
            //abstract interface public
            modify = String.class.getModifiers(); //java自带类:String
            System.out.println(modifierHelper(modify));
            //final public

            System.out.println("-------------------------------------获取类的包--------------------------------------------------");
            //获取类的名字
            String className1 = clazz1.getName();//获得全名:  包名+类名
            System.out.println(className1);
            //reflect.TestClass
            String className2 = clazz1.getSimpleName();//获得类名:  类名
            System.out.println(className2);
            //TestClass

            //获取类所属的包
            Package p = clazz1.getPackage(); //获取包
            System.out.println(p.getName()); //获取名字
            //reflect

            System.out.println("-------------------------------------获取类的父类--------------------------------------------------");
            //获取父类
            Class parent = clazz1.getSuperclass();//虽然我这个测试类没有用extends,但是所有类默认继承Object
            System.out.println(parent.getName());
            //java.lang.Object
            parent = ArrayList.class.getSuperclass();//试试ArrayList
            System.out.println(parent.getName());
            //java.util.AbstractList
            System.out.println(ArrayList.class.getSuperclass().getSuperclass());
            //class java.util.AbstractCollection 还可以找到父类的父类

            System.out.println("-------------------------------------获取类接口--------------------------------------------------");
            //获取接口,因为一个类可以有多个接口,所以可以返回多个类,用数组返回
            Class[] cArray = ArrayList.class.getInterfaces();
            for(Class c: cArray){
                System.out.println(c.getName());
            }
            //java.util.List
            //java.util.RandomAccess
            //java.lang.Cloneable
            //java.io.Serializable

            //ArrayList的源代码:public class ArrayList<E> extends AbstractList<E>
            //        implements List<E>, RandomAccess, Cloneable, java.io.Serializable


            System.out.println("-------------------------------------获取类的属性--------------------------------------------------");
            //获取class中的属性
            Field f1 = clazz1.getField("age"); //参数为属性名字
            //但是不能取到testName,因为那个是private的    (这也是为什么说为了安全属性最好设为private的原因之一。)

            Class fType = f1.getType();//返回属性的数据类型Class
            System.out.println(fType.getName());
            //int       因为是基本类型所以没有包名一类的。

            String s =f1.getName();//返回属性的名字
            System.out.println(s);
            //age

            //返回所有的属性
            Field[] fArr1 = clazz1.getFields();
            for(Field f: fArr1){
                System.out.println(f.getType() + " " + f.getName());
            }
            //int age
            //不能返回私有的

            Field[] fArr2 = clazz1.getDeclaredFields();
            for(Field f: fArr2){
                System.out.println(f.getType() + " " + f.getName());
            }
            //class java.lang.String testName
            //int age
            //返回了私有的属性!


            System.out.println("-------------------------------------操作类的属性--------------------------------------------------");
            //属性.赋值(对象,值)
            //用映射方法声明一个对象
            TestClass tc2 = (TestClass)clazz1.getDeclaredConstructor().newInstance();
            //之前是直接clazz1.newInstance(),现在过时了,不过也可以用.
            // clazz1.newInstance()其实就是默认调用了无参数构造方法,如果没有无参数的构造方法会报错。
            //clazz1.getDeclaredConstructor().newInstance();就算没有定义无参数构造方法也不会报错。

            System.out.println(tc2.show());
            //nname = null|age = 0   没有定义声明对象属性的值,所以自动赋给了一个默认值

            Field f2 = clazz1.getField("age"); //把TestClass的属性赋给了f2
            f2.set(tc2,222);//f2操作的是TestClass的age属性,这里给对象tcTest的age属性赋值222.  属性.set(对象,值);   平时我们都是: 对象.属性 = 值
            //这种方法不能给private或者final类型的赋值。
            System.out.println(tc2.show());
            //name = null|age = 222   赋值成功

            Field f3 = clazz1.getDeclaredField("testName");
            f3.setAccessible(true); //f3是private的,默认不可外部直接操作,用映射存值和取值都不行 ,所以先把f3设为可以操作的
            f3.set(tc2,"bbb"); //给f3赋值
            System.out.println(tc2.show());
            //name = bbb|age = 222   赋值成功

            String nameValue = (String)f3.get(tc2);   //属性.get(Object);  用来取对象的属性值,返回的是object对象,需要强转
            System.out.println(nameValue);
            //bbb     赋值成功

            //这里就可以看出映射的作用了,可以让你直接从外部操作私有变量。

            //比较:
            //getDeclaredField
            //可以获取当前类公有的和私有的属性
            //getField
            //可以获取当前类的公有属性(包括继承过来的属性)


            System.out.println("-------------------------------------搞事开始,修改String的值--------------------------------------------------");
            //学会了映射以后可以开始搞事
            //String 的内部结构是private final byte[] value;数组,所以是不能改变长度和值的,因此每次给String赋值其实是默认new了一个String
            //其实就是char[]数组,char和byte可以直接转换,所以下面为了直观直接用char[]操作。
            //因此学校里学的时候String具有不可变性。不过实际上用反射机制是可以变的
            String str1 = "aaa";
            String str2 = str1;  //引用变量赋值赋的是地址
            System.out.println(str1 == str2);
            //true
            str1 = "bbb";
            System.out.println(str1);
            System.out.println(str1);
            System.out.println(str1 == str2);
            //false
            // == 比较的是地址,str1改变了值后地址发生了改变

            //如果我们修改完String后,地址str1 == str2仍然为true,则说明地址没变,只是内容变了。
            //声明2个对象
            String str3 = "aaa";
            String str4 = str3;
            System.out.println(str3 == str4);
            //true
            //获取String 的 class
            Class strClass = str3.getClass();
            //获取属性
            Field str3_f = strClass.getDeclaredField("value");
            //赋予权限
            str3_f.setAccessible(true);
            //获取属性char[],数组是引用变量,final修饰后不能修改地址,但是可以修改值
            byte[] cArr = (byte[])str3_f.get(str3); //把str3中的char[]取出来
            cArr[0] = 'b';
            cArr[1] = 'b';
            cArr[2] = 'b';
            //不修改地址,直接修改数组内的值
            //长度不能修改

            System.out.println("str3 = " + str3);
            System.out.println("str4 = " + str4);
            System.out.println(str3 == str4);
            //str3 = bbb
            //str4 = bbb
            //true
            //修改成功,不过报了warning,说进行了不合理的操作 =w= 确实不太合理... final private的变量就这么从外部被修改了= =
            //所以尝试一下就好了,平时开发用不到,纯粹搞事情。
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static String modifierHelper(int i){
        // 一个类的modifier返回的数字,是符合以下要求的数字之和。
        //1024: abstract
        //512: interface
        //256: native
        //128: transient
        //64: volatile
        //32: synchronized
        //16: final
        //8: static
        //4: protected
        //2: private
        //1: public
        //0 默认不写
        String s = "";

        if(i >= 1024){
            s = s + "abstract ";
            i = i%1024;
        }
        if(i >= 512){
            s = s + "interface ";
            i = i%512;
        }
        if(i >= 256){
            s = s + "native ";
            i = i%256;
        }
        if(i >= 128){
            s = s + "transient ";
            i = i%128;
        }
        if(i >= 64){
            s = s + "volatile ";
            i = i%64;
        }
        if(i >= 32){
            s = s + "synchronized ";
            i = i%32;
        }
        if(i >= 16){
            s = s + "final ";
            i = i%16;
        }
        if(i >= 8){
            s = s + "static ";
            i = i%8;
        }
        if(i >= 4){
            s = s + "protected ";
            i = i%4;
        }
        if(i >= 2){
            s = s + "private ";
            i = i%2;
        }
        if(i == 1){
            s = s + "public ";
        }
        return s;
    }
}

 

package reflect;

public class TestClass {
    //属性
    private String testName;
    public int age;
    //构造方法
    public TestClass(){
    }
    public TestClass(String name, int age){
        this.testName = name;
        this.age = age;
    }
    //
    {
        System.out.println("我在构造函数之前运行");
    }
    //方法
    //

    public String getTestName() {
        return testName;
    }

    public void setTestName(String name) {
        this.testName = name;
    }

    public String show(){
        return "name = " + this.testName + "|age = " + this.age;
    }
}

 

posted @ 2019-10-11 16:51  不咬人的兔子  阅读(210)  评论(0编辑  收藏  举报