【第一阶段 day43 API】 反射 内部类

1.反射

1.概念

(1)当我们想要使用别人的东西/查看某些资源的时候,可以使用反射技术
再比如,开发时,有时候不能看到源代码,也可以通过反射获取

2.反射的前提:获取字节码对象

(1)在java中可以将对象分为两类:字节码对象和实例对象
(2)字节码对象获取的三种方式:

Class.forName(“目标类的全路径”)
类名.class
目标类对象.getClass()

注意:字节码对象是获取目标对象所有信息的入口

3.反射的常用方法

3.1 通过单元测试方法,获取目标类对应的字节码对象

(1)获取包对象 System.out.println(student2.getPackage());
(2)先获取包对象,再获取包对象的名字
System.out.println(student3.getPackage().getName());
(3)获取字节码对象
System.out.println(student1);
(4)打印全路径名
System.out.println(student1.getName());
(5)打印只有目标类的类名
System.out.println(student2.getSimpleName());

    //1.通过单元测试方法,获取目标类Student对应的字节码对象
    @Test
    public void getCalzz() throws ClassNotFoundException {
        Class<?> student1 = Class.forName("cn.tedu.review.Student");
        Class<?> student2 = Student.class;
        Class<?> student3 = new Student().getClass();
        /*1>获取字节码对象*/
        System.out.println(student1);//class cn.tedu.review.Student
        /*2>获取通过字节码对象获取到的包对象*/
        System.out.println(student2.getPackage());//package cn.tedu.review
        /*3>获取通过包对象获取到的包的名字*/
        System.out.println(student3.getPackage().getName());//cn.tedu.review
        /*4>获取通过字节码对象获取到的全路径名*/
        System.out.println(student1.getName());//cn.tedu.review.Student
        /*5>获取通过字节码对象获取到的类名*/
        System.out.println(student2.getSimpleName());//Student
    }

3.2 通过单元测试方法,练习引用类型数组的定义与遍历

 //2.通过单元测试方法,练习引用类型数组的定义与遍历
    @Test
    public void getStu() throws ClassNotFoundException {
        //2.1 创建Student三个对象
        Student s1=new Student("张三",3);
        Student s2=new Student("李四",4);
        Student s3=new Student("王五",5);

        //2.2 创建数组,将刚刚创建的三个对象存入数组中
        Student[] s=new Student[]{s1,s2,s3};
        //2.3 直接打印数组,拿到没一个学生对象,做进一步操作
        System.out.println(Arrays.toString(s));
        //2.4 遍历学生数组,拿到没一个学生对象
        for (Student n:s ) {
            //System.out.println(n);//拿到的是对象
            n.sunDay(3);
            System.out.println(n.age);
            System.out.println(n.getName());
        }
    }

3.3 通过单元测试方法,获取目标类的成员方法

(1)获取所有可见方法,包括继承方法

//3.通过单元测试方法,获取Student类中的成员方法
    @Test
    public void getFunction() throws ClassNotFoundException {
        //3.1 获取字节码对象
        Class<?> s1 = Class.forName("cn.tedu.review.Student");
        Class<?> s2 = Student.class;
        Class<?> s3 = new Student().getClass();
        //3.2 通过字节码对象,获取目标类的成员方法们
        Method[] ms = s1.getMethods();//获取所有可见的方法,包括继承的方法
        System.out.println(Arrays.toString(ms));
        //3.3 通过高效for循环遍历数组,拿到一个方法对象
        for(Method m:ms){
            System.out.println(m);//直接打印遍历到的方法对象
            System.out.println(m.getName());//通过方法对象获取方法名
            Class<?>[] pt = m.getParameterTypes();//通过方法对象获取方法所有参数的数组
            System.out.println(pt);
        }
        Method[] dm = s1.getDeclaredMethods();
    }

3.4 通过单元测试方法,获取目标类的构造方法

//4.通过单元测试方法,获取Student类中的构造方法
    @Test
    public void getConstrutor()  throws ClassNotFoundException {
        //4.1 获取字节码对象
        Class<?> s = Class.forName("cn.tedu.review.Student");
        //4.2 通过字节码对象获取目标类的构造方法们
        Constructor<?>[] cs = s.getConstructors();
        //4.3 通过高效for循环遍历数组
        for (Constructor c:cs){
            System.out.println(c);
            System.out.println(c.getName());//打印本轮遍历到的构造方法的名字
            Class[] pt = c.getParameterTypes();//通过遍历到的构造函数对象获取构造函数参数的类型
            System.out.println(pt);
        }
    }

3.5 通过单元测试方法,获取目标类的成员变量

    //5.通过单元测试方法,获取Student类中的成员变量
    @Test
    public void getFie(){
        //5.1 获取字节码对象
        Class<?> s1 = Student.class;
        //5.2 通过字节码对象获取成员变量们
        Field[] fs = s1.getFields();
        //5.3 遍历数组
        /**
         * 注意:目前成员变量的修饰符必须是public才能获取到
         * 默认修饰符也是获取不到的
         */
        for (Field f:fs) {
            System.out.println(f.getName());//通过本轮循环到的字段对象获取字段名
            System.out.println(f.getType());//通过本轮循环的字段对象获取字段类型
        }
    }

3.6 通过单元测试方法,创建目标类对象

//6.通过单元测试方法,创建Student目标类的对象
    @Test
    public void getObject() throws Exception{
        //6.1 获取字节码对象
        Class<?> s = Student.class;

        //6.2 通过反射技术,创建目标类的对象
        /**
         * 反射创建对象方案1:通过触发目标类的无参构造来创建对象
         * 对象创建了,但是没有值
         */
        Object o = s.newInstance();//这一步已经获取了对象
        System.out.println(o);//Student{name='null', age=0}

        //6.3 尝试通过其他构造函数来创建对象
        /**
         * 反射创建对象方案2:通过触发目标类的全参构造创建对象:
         * 1.先获取指定的构造函数,注意需要指定构造函数的参数,传入的是.class字节码对象
         * 2.通过刚刚获取到的构造函数对象,创建Student目标类的对象,并给对象的属性赋值
         */
        //6.3.1 想要其他构造,获取方式:指定参数列表来获取
        Constructor<?> ct = s.getConstructor(String.class, int.class);
        System.out.println(ct);
        //6.3.2 通过构造函数,来创建对象
        Object o2 = ct.newInstance("海绵宝宝", 12);
        System.out.println(o2);

    }

完整代码

package cn.tedu.review;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 这个类用来复写反射的测试类
 */
public class TestReflect {
    /**
     * 1.单元测试方法:public+void+没有参数+@Test
     */
    //1.通过单元测试方法,获取目标类Student对应的字节码对象
    @Test
    public void getCalzz() throws ClassNotFoundException {
        Class<?> student1 = Class.forName("cn.tedu.review.Student");
        Class<?> student2 = Student.class;
        Class<?> student3 = new Student().getClass();
        /*1>获取字节码对象*/
        System.out.println(student1);//class cn.tedu.review.Student
        /*2>获取通过字节码对象获取到的包对象*/
        System.out.println(student2.getPackage());//package cn.tedu.review
        /*3>获取通过包对象获取到的包的名字*/
        System.out.println(student3.getPackage().getName());//cn.tedu.review
        /*4>获取通过字节码对象获取到的全路径名*/
        System.out.println(student1.getName());//cn.tedu.review.Student
        /*5>获取通过字节码对象获取到的类名*/
        System.out.println(student2.getSimpleName());//Student
    }
    //2.通过单元测试方法,练习引用类型数组的定义与遍历
    @Test
    public void getStu() throws ClassNotFoundException {
        //2.1 创建Student三个对象
        Student s1=new Student("张三",3);
        Student s2=new Student("李四",4);
        Student s3=new Student("王五",5);

        //2.2 创建数组,将刚刚创建的三个对象存入数组中
        Student[] s=new Student[]{s1,s2,s3};
        //2.3 直接打印数组,拿到没一个学生对象,做进一步操作
        System.out.println(Arrays.toString(s));
        //2.4 遍历学生数组,拿到没一个学生对象
        for (Student n:s ) {
            //System.out.println(n);//拿到的是对象
            n.sunDay(3);
            System.out.println(n.age);
            System.out.println(n.getName());
        }
    }
    //3.通过单元测试方法,获取Student类中的成员方法
    @Test
    public void getFunction() throws ClassNotFoundException {
        //3.1 获取字节码对象
        Class<?> s1 = Class.forName("cn.tedu.review.Student");
        Class<?> s2 = Student.class;
        Class<?> s3 = new Student().getClass();
        //3.2 通过字节码对象,获取目标类的成员方法们
        Method[] ms = s1.getMethods();//获取所有可见的方法,包括继承的方法
        System.out.println(Arrays.toString(ms));
        //3.3 通过高效for循环遍历数组,拿到一个方法对象
        for(Method m:ms){
            System.out.println(m);//直接打印遍历到的方法对象
            System.out.println(m.getName());//通过方法对象获取方法名
            Class<?>[] pt = m.getParameterTypes();//通过方法对象获取方法所有参数的数组
            System.out.println(pt);
        }
        Method[] dm = s1.getDeclaredMethods();
    }
    //4.通过单元测试方法,获取Student类中的构造方法
    @Test
    public void getConstrutor()  throws ClassNotFoundException {
        //4.1 获取字节码对象
        Class<?> s = Class.forName("cn.tedu.review.Student");
        //4.2 通过字节码对象获取目标类的构造方法们
        Constructor<?>[] cs = s.getConstructors();
        //4.3 通过高效for循环遍历数组
        for (Constructor c:cs){
            System.out.println(c);
            System.out.println(c.getName());//打印本轮遍历到的构造方法的名字
            Class[] pt = c.getParameterTypes();//通过遍历到的构造函数对象获取构造函数参数的类型
            System.out.println(pt);
        }
    }
    //5.通过单元测试方法,获取Student类中的成员变量
    @Test
    public void getFie(){
        //5.1 获取字节码对象
        Class<?> s1 = Student.class;
        //5.2 通过字节码对象获取成员变量们
        Field[] fs = s1.getFields();
        //5.3 遍历数组
        /**
         * 注意:目前成员变量的修饰符必须是public才能获取到
         * 默认修饰符也是获取不到的
         */
        for (Field f:fs) {
            System.out.println(f.getName());//通过本轮循环到的字段对象获取字段名
            System.out.println(f.getType());//通过本轮循环的字段对象获取字段类型
        }
    }
    //6.通过单元测试方法,创建Student目标类的对象
    @Test
    public void getObject() throws Exception{
        //6.1 获取字节码对象
        Class<?> s = Student.class;

        //6.2 通过反射技术,创建目标类的对象
        /**
         * 反射创建对象方案1:通过触发目标类的无参构造来创建对象
         * 对象创建了,但是没有值
         */
        Object o = s.newInstance();//这一步已经获取了对象
        System.out.println(o);//Student{name='null', age=0}

        //6.3 尝试通过其他构造函数来创建对象
        /**
         * 反射创建对象方案2:通过触发目标类的全参构造创建对象:
         * 1.先获取指定的构造函数,注意需要指定构造函数的参数,传入的是.class字节码对象
         * 2.通过刚刚获取到的构造函数对象,创建Student目标类的对象,并给对象的属性赋值
         */
        //6.3.1 想要其他构造,获取方式:指定参数列表来获取
        Constructor<?> ct = s.getConstructor(String.class, int.class);
        System.out.println(ct);
        //6.3.2 通过构造函数,来创建对象
        Object o2 = ct.newInstance("海绵宝宝", 12);
        System.out.println(o2);

    }
}

4.暴力反射
package cn.tedu.review;

import org.junit.Test;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 本类用来测试暴力反射
 */
public class TestReflect2{
    //1.通过暴力反射获取与操作属性
    @Test
    public void getFileds() throws Exception{
        //1.1 获取字节码对象
        Class<Person> p1 = Person.class;
        //1.2 获取私有属性
        Field name = p1.getDeclaredField("name");
        //1.3 根据刚刚获取到的属性对象,查看属性的信息
        System.out.println(name);//直接打印获取到的字段对象
        System.out.println(name.getType());//class java.lang.String
        System.out.println(name.getType().getName());//java.lang.String
        //1.4设置属性的值
        //1.4.1 需要指定到底给哪个对象的name属性设置值,没有对象就创建对象
        Person person1 = p1.newInstance();
        //1.4.2 暴力反射,需要设置私有可见权限
        name.setAccessible(true);
        //1.4.3 通过字段对象给刚刚创建好的对象persn1设置值为小白
        //name就是我们刚刚获取到的name属性
        //set(m,n) --  m是给哪个对象的name属性设置值,n是设置的值是什么
        name.set(person1,"小白");
        //1.4.4 打印查看刚刚设置的值
        System.out.println(name.get(person1));
    }
    //2.定义单元测试方法,利用暴力反射操作私有属性age
    @Test
    public void getFileds1() throws Exception{
        //2.1定义单元测试方法
        Class<Person> p= Person.class;
        //2.2获取指定的私有属性对象
        Field age = p.getDeclaredField("age");
        //2.3根据获取到的属性对象,查看相关信息
        System.out.println(age.getName());
        System.out.println(age.getType().getName());
        //2.4设置属性的值
        Person person = p.newInstance();
        age.setAccessible(true);
        age.set(person,12);
        System.out.println(age.get(person));
    }
    //3.创建单元测试方法,通过暴力反射获取与执行Person类的私有方法
    @Test
    public void getFunction() throws Exception {
        //3.1获取字节码对象
        Class<Person> p = Person.class;
        //3.2通过字节码对象,获取私有方法对象
        /**
         * 如何确定要找的是哪个方法?方法名+参数列表
         */
        Method save = p.getDeclaredMethod("save", int.class, String.class);
        //3. 在执行获取到的方法之前,需要先指定给哪个对象做这个save()操作
        //3.3.1 没有对象先创建对象
        Person obj = p.newInstance();
        //3.3.2 想要执行私有方法,也需要设置私有可见
        /**
         * 在执行私有的方法之前,需要设置私有可见的权限
         */
        save.setAccessible(true);
        //3.3.3 通过刚刚获取到的方法对象method给指定的对象obj进行操作
        save.invoke(obj,222,"hhh");
    }
}

2.内部类

2.1特点

1>内部类可以直接访问外部类中的成员,包括私有成员
2>外部类要访问内部类的成员,必须要建立内部类的对象
3>在成员位置的内部类为成员内部类
4>在局部位置的内部类为局部内部类

2.2 成员内部类

1>内部类可以直接使用外部类的资源,私有成员也可以
2>外部类如果想要使用内部类的资源,必须先创建内部类对象,通过内部类对象来调用内部类资源
3>创建内部类对象,使用内部类资源
外部类名.内部类名 对象名=外部类对象.内部类对象
练习1:测试内部类入门案例

package cn.tedu.innerclass;

/**
 * 本类用于内部类的入门案例
 */
public class TestInner1 {
    public static void main(String[] args) {
        //3.1创建内部类的对象,使用内部类的资源
        /**
         * 外部类名.内部类名 对象名=外部类对象.内部类对象
         */
        Outer.Inner oi=new Outer().new Inner();
        //3.2通过创建好的内部类对象,使用内部类的资源
        oi.delete();
        System.out.println(oi.sum);
        //3.3外部想要使用外部类的资源,需要通过外部类的对象
        new Outer().play();

    }
}
//1.创建外部类Outer
class Outer{
    //1.1创建外部类的成员变量
    String name;
    private int age;
    //1.2创建外部类的成员方法
    public void play(){
        System.out.println("outer...play()");
        //5.测试外部类能否使用内部类的资源
//        delete();//不能直接调用内部类的方法
//        System.out.println(sum);//不能直接查看内部类的属性
        /**
         * 外部类如果想要使用内部类的资源,必须先创建内部类的对象
         * 通过内部类对象调用内部类功能
         */
        Inner in=new Inner();//直接创建内部类对象,无需指定外部类,已经在外部类里了
        //in.delete();//通过内部类对象调用内部类方法
        System.out.println(in.sum);//通过内部类对象调用内部类变量
    }

    //2.创建内部类 -- 外部类的一个特殊成员
    class Inner{
        //2.1创建内部类的成员变量
        int sum=10;
        //2.2定义内部类的成员方法
        public void delete(){
            System.out.println("Inner...delete()");
            //4.测试内部类能否使用外部类的资源
            play();//可以查看外部类的方法
            System.out.println(name);//可以查看外部类的普通属性
            System.out.println(age);//可以查看外部类的私有属性
        }
    }
}
2.3 成员内部类(private修饰)

1>成员内部类被Private修饰以后,无法被外界直接创建对象使用
2>我们可以创建外部类对象,通过外部类对象间接王文内部类资源
练习2:测试成员内部类被private修饰

package cn.tedu.innerclass;

/**
 * 本类用于测试成员内部类被private修饰
 */
public class TestInner2 {
    public static void main(String[] args) {

        //4.创建内部类的对象
        /**
         * 2.如果成员内部类被private修饰,外部无法直接访问或者创建内部类的对象
         */
        //7.间接访问,虽然不可以创建私有内部类的对象,但是可以创建外部类的对象
        new Outer2().getInner2Eat();
//        Outer2.Inter2 oi=new Outer2().new Inter2();
//        oi.eat();
    }
}
//1.创建外部类
class Outer2{
    //6.提供一个公共的方法,在方法内部创建内部类的对象,调用内部类的功能
    public void getInner2Eat(){
        Inter2 in2=new Inter2();//可以在外部类里创建私有成员内部的对象
        in2.eat();//通过创建好的内部类对象调用内部类功能
    }
    /**
     * 1.成员内部类的位置在内里方法外
     */
    //2.创建成员内部类
    //5.用private修饰内部类
    private class Inter2{
        //3.创建内部类的普通方法
       public void eat(){
           System.out.println("Inner2...eat()");
       }
   }
}
2.4 成员内部类(static修饰)

1>静态资源访问时不需要创建对象,可以通过类名直接访问
2>访问静态类中的静态资源可以通过"…"链式加载的方式访问

package cn.tedu.innerclass;

/**
 * 本类用来测试成员内部类被static修饰
 */
public class TestInner3 {
    public static void main(String[] args) {
        //4.创建内部类对象
        //方式1:创建内部类对象调用show()
//        Outer3.Inter3 oi=new Outer3().new Inter3();
//        oi.show();
        //方式2:创建内部类匿名对象访问show()
//        new Outer3().new Inter3().show();

        /**
         * 现象:当内部类被static修饰以后,new Outer3报错
         */
        //6.用static修饰内部类以后,上面的创建语句报错
        Outer3.Inter3 oi=new Outer3.Inter3();
        oi.show();

        //7.匿名内部类对象调用show()
        new Outer3.Inter3().show();

        //9.访问静态内部类中的静态资源---链式加载
        /**
         * 没有创建任何一个对象,直接都是通过类名找到的静态资源
         * 像这样连着点的方式,就是:链式加载
         */
        Outer3.Inter3.show2();
    }
}
//1.创建外部类
class Outer3{
    //2.创建成员内部类
    //5.内部类被static修饰---不常用,浪费内存
    static class Inter3{
        //3.定义成员内部类中的普通成员方法
        public void show(){
            System.out.println("Inner...show()");
        }
        //8.定义成员内部类的静态成员方法
        static public void show2(){
            System.out.println("Inner...show2()");
        }
    }
}
2.5 局部内部类
package cn.tedu.innerclass;

/**
 * 本类用于测试局部内部类
 */
public class TestInner4 {
    public static void main(String[] args) {
        //5.创建外部类对象调用
        new Outer4().show();
        /**
         * 如何使用局部内部类的资源呢?
         * 创建外部类对象调用外部方法或者在main()创建局部内部类的对象都是不可行的
         * 需要在外部类中创建内部类的对象,并且调用内部类的方法,才会触发内部类的功能
         */
    }
}
//1.创建外部类
class Outer4{
    //2.创建外部类的成员方法
    public void show(){
        System.out.println("Outer4...show()");
        //3.创建局部内部类
        class Inter4{
            //4.创建局部内部类的属性与方法
            String name;
            int age;
            public void eat(){
                System.out.println("Inter4...eat()");
            }
        }
        /**
         * 如何使用局部内部类的资源?
         * 在show方法里,创建内部类对象,调用内部类的功能
         */
        Inter4 i=new Inter4();
        i.eat();
        System.out.println(i.name);
        System.out.println(i.age);
    }
}
2.6 匿名内部类
package cn.tedu.innerclass;

/**
 * 本类用于测试匿名内部类
 * 匿名内部类没有名字,通常和匿名对象结合在一起使用
 */
public class TestInner5 {
    public static void main(String[] args) {
        //3.创建接口1对应的匿名对象与匿名内部类
        new Inter(){

            @Override
            public void save() {
                System.out.println("save");
            }

            @Override
            public void get() {
                System.out.println("get");
            }
        }.get();
        //5.创建抽象类对应的匿名对象与匿名内部类
        new Inter2() {
            @Override
            public void drink() {
                System.out.println("酒");
            }
        }.drink();
        //7.调用普通类的功能(创建匿名对象,直接调用)
        new Inter3().power();
        new Inter3().power();//new了两次,是两个匿名对象
        /**
         * 如果想要多次使用实现后的功能,还是要创建普通的对象
         * 匿名对象只能使用一次,一次只能调用一个功能
         * 匿名内部类其实就充当了实现类的角色,去实现未实现的抽象方法,只是没有名字而已
         */
        Inter3 i3=new Inter3();
        i3.power();
        i3.study();
    }
}
//1.创建接口
interface Inter{
    //2.定义接口中的抽象方法
    void save();
    void get();
//    default void eat(){
//        System.out.println("我是默认方法");
//    }
}
//4.创建抽象类
abstract class Inter2{
    public void play(){
        System.out.println("inter2...play");
    }
    abstract public void drink();
}
//6.创建普通类
class Inter3{
    public void power(){
        System.out.println("锵锵锵");
    }
    public void study(){
        System.out.println("嘻嘻嘻");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值