Class类API

2021/02/15 Java初级进阶 共 19439 字,约 56 分钟
闷骚的程序员

1. Class类的api学习

1.1 准备几个操作类

MyInterface:

package zeh.test.demo.com.class1;

// 顶层接口
public interface MyInterface {
    void abstractFun();
    void eat();
    void drink();
    void sleep();
}

ClassA:

package zeh.test.demo.com.class1;

// classA实现MyInterface
public class ClassA implements MyInterface {
    @Override
    public void abstractFun() {
        System.out.println("ClassA implements");
    }
    @Override
    public void eat() {
        System.out.println("ClassA eat");
    }
    @Override
    public void drink() {
        System.out.println("ClassA drink");
    }
    @Override
    public void sleep() {
        System.out.println("ClassA sleep");
    }
    public void testA() {
        System.out.println("ClassA testA");
    }
}

ClassB:

package zeh.test.demo.com.class1;

// ClassB 继承 ClassA
public class ClassB extends ClassA {
    @Override
    public void abstractFun() {
        System.out.println("ClassB implements");
    }
    @Override
    public void drink(){
        System.out.println("ClassB drink");
    }
    public void testB() {
        System.out.println("ClassB testB");
    }
}

ClassC:

package zeh.test.demo.com.class1;

public class ClassC extends ClassB {
    @Override
    public void abstractFun() {
        System.out.println("ClassC implements");
    }
    @Override
    public void sleep(){
        System.out.println("ClassC sleep");
    }
    public void testC() {
        System.out.println("ClassC testC");
    }
}

ClassD:

package zeh.test.demo.com.class1;

public class ClassD extends ClassC {
    @Override
    public void abstractFun() {
        System.out.println("ClassD implements");
    }
    public void testD() {
        System.out.println("ClassD testD");
    }
}

1.2 asSubclass方法

  1. 方法定义如下:
    public <U> Class<? extends U> asSubclass(Class<U> clazz);
    
  2. 说明:将调用这个方法的class对象转换成由clazz参数所表示的class对象或者由clazz参数所表示的子类的class对象。
  3. 举例:
    List<String> myList = new ArrayList<String>();
    Class<? extends List> sub_class = myList.getClass().asSubclass(List.class);
    

    该案例是将myList表示的class对象,转换成了List接口所表示的class对象的某个子类。
    即将myList.getClass()获取的class对象转换成了Class<? extends List>。
    fuck,毫无意义。因为我们很清楚myList.getClass()获取的class对象就是ArrayList,它当然是List.class的一个子类;
    搞鸡毛啊,这有啥意义?玩我?

  4. 但有些情况下,我们并不能确知一个class对象的类型,典型的情况是Class.forName()获取的class对象:
    Class.forName()的返回类型是Class<?>,但这显然太宽泛了,假设我们需要List.class类型的class对象,但我们传递给Class.forName的参数是未知的(可能是”java.lang.String”,也可能是”java.util.ArrayList”),这时我们就可以用到asSubclass()这个方法了,如下:
    Class.forName("xxx.xxx.xxx").asSubclass(List.class).newInstance();
    

    当xxx.xxx.xxx是List的子类时,正常执行,当不是List的子类时,抛出ClassCastException,这时我们可以做些别的处理;
    如果我们查看Class.asSubclass()在JDK中的中的引用的话,会发现这种用法有很多。

  5. 总结:
    对于未知的class实例类型,比如由外部传入进来的,我们需要使用asSubclass来窄化未知的Class类型的范围,即对未知的class对象所属的类型范围进行一个窄化的限制,让它固定属于某个类型范围;如果不属于应该去做其他处理。
    说白了,asSubclass就是一个判断,判断当前的class对象是否是目标class对象所表示的类型或者是目标class对象所表示的类型的子类型。
  6. 测试:
    package zeh.test.demo.com.class1;
        
    public class TestClassApi {
        public static void main(String[] args) {
            // 当前class对象是 ClassA.class,它是目标class对象 ClassA.class 所表示的类型,因此返回的是 ClassA.class 所表示的类型。
            Class<? extends ClassA> classA = ClassA.class.asSubclass(ClassA.class);
            System.out.println(classA.getSimpleName());
            // 当前class对象是 ClassA.class,它是目标class对象 MyInterface.class 所表示的类型的子类型,因此返回的是 MyInterface.class 所表示的某个子类型,即 Class<? extends MyInterface>。
            Class<? extends MyInterface> myInterface = ClassA.class.asSubclass(MyInterface.class);
            System.out.println(myInterface.getSimpleName());
            // 当前class对象是 ClassA.class,它不是目标class对象 ClassD.class 所表示的类型,因此抛出异常 Exception in thread "main" java.lang.ClassCastException: class zeh.test.demo.com.class1.ClassA
            Class<? extends ClassD> classD = ClassA.class.asSubclass(ClassD.class);
            System.out.println(classD.getSimpleName());
        }
    }
    
  7. 测试结果:
    ClassA
    ClassA
    Exception in thread "main" java.lang.ClassCastException: class zeh.test.demo.com.class1.ClassA
        at java.lang.Class.asSubclass(Class.java:3404)
        at zeh.test.demo.com.class1.TestClassApi.main(TestClassApi.java:21)
    Process finished with exit code 1
    

1.3 cast方法

  1. 方法定义如下:
    public T cast(Object obj);
    
  2. 说明:将目标对象obj转换为当前class对象所表示类或者接口类型。
  3. 返回值:目标对象强制转换后的对象,如果目标对象是null,则返回null。
  4. java中存在类型转换,分为自动类型转换和强制类型转换,cast方法实际上就是对类型转换的编码式实现(自动转换和强制转换的编码式实现)。即cast方法实际上就是将目标对象转换为当前class表示的类型,转换成功则返回转换后的对象,规则直接抛出类型转换异常。
  5. 测试:
    package zeh.test.demo.com.class1;
        
    public class TestClassApi {
        public static void main(String[] args) {
            // 目标对象
            ClassC classC = new ClassC();
            // 当前类型:ClassA,将目标对象classD强制转换为当前类型 ClassA
            // 仔细观察,此处实际上就是向上转型
            ClassA classA = ClassA.class.cast(classC);
            classA.eat();
            // 此处实际上就是向下转型,向下转型前必须进行向上转型
            classC = ClassC.class.cast(classA);
            classC.testC();
            // 此处实际上就是向下转型,将classA向下转型为ClassD,因为前面没有发生继承链上对应的向上转型,因此抛出类型转换异常
            try {
                ClassD classD = ClassD.class.cast(classA);
                classD.testD();
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 如果目标对象是null值,则直接返回null值
            Object obj = ClassD.class.cast(null);
            System.out.println(obj);
            // 目标对象是基本类型,则会自动装箱,装箱后再进行强制转换
            Integer integer = Integer.class.cast(124);
            System.out.println(integer);
        }
    }
    
  6. 测试结果:
    ClassA eat
    ClassC testC
    null
    124
    java.lang.ClassCastException: Cannot cast zeh.test.demo.com.class1.ClassC to zeh.test.demo.com.class1.ClassD
        at java.lang.Class.cast(Class.java:3369)
        at zeh.test.demo.com.class1.TestClassApi.main(TestClassApi.java:26)
    Process finished with exit code 0
    

1.4 getCanonicalName方法

  1. 方法定义如下:
    public String getCanonicalName();
    
  2. 说明:返回 Java Language Specification 中所定义的底层类的规范化名称。如果底层类没有规范化名称(即如果底层类是一个组件类型没有规范化名称的本地类、匿名类或数组),则返回 null。
  3. 返回值:底层类的规范化命名,实际上就是一个类的权限定名称。
  4. 测试:
    package zeh.test.demo.com.class1;
        
    public class TestClassApi {
        public static void main(String[] args) {
            System.out.println(ClassA.class.getCanonicalName());
        }
    }
    
  5. 测试结果:
    zeh.test.demo.com.class1.ClassA
    Process finished with exit code 0
    

1.5 getComponentType方法

  1. 方法定义如下:
    public Class<?> getComponentType();
    
  2. 说明:如果当前class表示的是数组类,则返回表示该数组类型的class对象,否则返回null。
  3. 返回值:一个class对象,如果当前类表示数组类,则返回该数组类型的class对象,否则返回null值。
  4. 测试:
    package zeh.test.demo.com.class1;
        
    public class TestClassApi {
        public static void main(String[] args) {
            // 当前class对象不表示数组,则返回null值
            System.out.println(ClassA.class.getComponentType());
            // 当前class对象表示数组,则返回该数组对应的class对象。
            System.out.println(new ClassA[]{}.getClass().getComponentType());
        }
    }
    
  5. 测试结果:
    null
    class zeh.test.demo.com.class1.ClassA
    Process finished with exit code 0
    

1.6 isAssignableFrom方法

  1. 方法定义如下:
    public boolean isAssignableFrom(Class<?> cls);
    
  2. 说明:当前class对象所描述的类或者接口,是否与指定的cls对象描述的类或者接口相同,或者是其父类或者父接口。
    如果是则返回true,否则返回false。如果当前class对象表示一个基本类型,则目标cls也必须表示对应的基本类型,否则返回false。
    如果指定的cls参数为null,将抛出空指针异常。
  3. 返回值:true或者false。
  4. 补充:isAssignableFrom表示“我是不是你爸爸”。
    instanceof测试左边的对象是否能够强制转换成右边的类型,而 isAssignableFrom 实际上直接测试的是左边的类型是否是右边的类型,或者是右边类型的父类型。
  5. 测试:
    package zeh.test.demo.com.class1;
        
    public class TestClassApi {
        public static void main(String[] args) {
            System.out.println("ClassA 是 MyInterface 或者其爸爸?" + ClassA.class.isAssignableFrom(MyInterface.class));
            System.out.println("ClassA 是 ClassC 或者其爸爸?" + ClassA.class.isAssignableFrom(ClassC.class));
            System.out.println("ClassA 是 int 或者其爸爸?" + ClassA.class.isAssignableFrom(int.class));
            System.out.println("Integer 是 int 或者其爸爸?" + Integer.class.isAssignableFrom(int.class));
            System.out.println("int 是 Integer 或者其爸爸?" + int.class.isAssignableFrom(Integer.class));
            System.out.println("int 是 char 或者其爸爸?" + int.class.isAssignableFrom(char.class));
            System.out.println("int 是 int 或者其爸爸?" + int.class.isAssignableFrom(int.class));
            // 目标class对象为null值,将抛出空指针异常
            System.out.println(double.class.isAssignableFrom(null));
        }
    }
    
  6. 测试结果:
    ClassA 是 MyInterface 或者其爸爸?false
    ClassA 是 ClassC 或者其爸爸?true
    ClassA 是 int 或者其爸爸?false
    Integer 是 int 或者其爸爸?false
    int 是 Integer 或者其爸爸?false
    int 是 char 或者其爸爸?false
    int 是 int 或者其爸爸?true
    Exception in thread "main" java.lang.NullPointerException
        at java.lang.Class.isAssignableFrom(Native Method)
        at zeh.test.demo.com.class1.TestClassApi.main(TestClassApi.java:22)
    Process finished with exit code 0
    

1.7 isInstance方法

  1. 方法定义如下:
    public boolean isInstance(Object obj);
    
  2. 说明:目标obj对象是否是当前class对象所表示的类型。实际上,该方法是instanceof的等价实现。如果当前class对象表示基本类型,则直接返回false。
  3. 区别:
    obj instanceof TargetClass表示左边的obj是否能够强转成TargetClass类型  
    class.isInstance(Object obj):表示目标对象obj是否能够强制转换为当前的Class类型  
    

    只是将表示顺序颠倒了而已,本质上都表示一个对象是否能够强制转换为目标类型。

2. 玩转isPrimitive方法

之所以将isPrimitive方法单独拎出来说,是因为 isPrimitive 方法牵扯到Class类、Field类、包装类、JVM内置的9个class对象等基本类型的复杂关系,需要一步步深入搞懂。

2.1 isPrimitive方法能干啥?

  1. 方法定义如下:
    public boolean isPrimitive();
    
  2. 说明:判定指定的 Class 对象是否表示一个基本类型。有九种预定义的 Class 对象,表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即 boolean、byte、char、short、int、long、float 和 double。
  3. 返回值:如果是上述9种class对象的话,则返回true,否则返回false。
  4. 备注:上面的9种class对象,只能通过public static final类型的字段进行访问,不能通过getClass()方法或者Class.forName()进行访问。比如:
    int.class,void.class,boolean.class等。
    并且,整个java中也就只有这9种class对象能够使isPrimitive()方法返回true。
  5. isPrimitive()总结下,就是来判断传入的一个class对象是否是基本类型和void类型的。
  6. 测试:
    ```java package zeh.test.demo.com; /**
    • 功能描述 *
    • @author zWX5331241
    • @since 2020-06-24 */ public class Test { public static void main(String args[]) { System.out.println(void.class.isPrimitive()); System.out.println(int.class.isPrimitive()); System.out.println(float.class.isPrimitive()); System.out.println(boolean.class.isPrimitive()); System.out.println(Integer.class.isPrimitive()); } } ```
  7. 测试结果:
    true
    true
    true
    true
    false
    Process finished with exit code 0
    
  8. 疑问?
    上面案例中,凡是JVM预创建的9种class对象,isPrimitive都返回了true。
    然而,对于基本类型的包装类,比如Integer,它的class对象通过 isPrimitive 竟然返回了false。
    而往往,实际业务中,我们认为8大基本类型的包装类,也应该是我们认知当中的基本类型,那显然,此处单纯通过 isPrimitive 方法去验证,对于包装类就失效了。
    对于包装类,我们该如何进行验证?
    我们进一步看。

2.2 class对象直接输出

  1. 任何一个class对象直接输出,则表示该class对象表示的权限定名称,即它表示的类链接符。因为Class的toString()方法实现了覆盖。
  2. Class类覆盖后的toString方法如下:
    public String toString() {
        return (isInterface() ? "interface " : (isPrimitive() ? "" : "class ")) + getName();
    }
    
  3. 测试:
    package zeh.test.demo.com;
        
    public class Test {
        public static void main(String args[]) {
            System.out.println("Person.class is : " + Person.class);
            System.out.println("Integer.class is : " + Integer.class);
            System.out.println("int.class is : " + int.class);
            System.out.println("void.class is : " + void.class);
            System.out.println("boolean.class is : " + boolean.class);
            System.out.println("char.class is : " + char.class);
        }
    }
    
  4. 测试结果:
    Person.class is : class zeh.test.demo.com.Person
    Integer.class is : class java.lang.Integer
    int.class is : int
    void.class is : void
    boolean.class is : boolean
    char.class is : char
    Process finished with exit code 0
    
  5. 结果分析:
    通过测试可以发现,如果是JVM预创建的9种内置class对象的话,其class对象直接输出的话,直接输出的就是其表示的基本类型或者void的名称。即,int.class直接输出就是int,void.class直接输出就是void。
    如果不是预创建的9种内置class对象,而是普通的引用类型的class对象,则class对象直接输出的格式为:class class对象描述的类的权限定名称。

2.3 Field对象直接输出

  1. Field类用来描述一个类里面的成员变量。
  2. Field类的toString()方法实现了覆盖,直接输出一个field对象,就是输出该field对象描述的字段的权限定信息。
  3. Field类的toString()方法覆盖如下:
    public String toString() {
        int mod = getModifiers();
        return (((mod == 0) ? "" : (Modifier.toString(mod) + " "))
            + getType().getTypeName() + " "
            + getDeclaringClass().getTypeName() + "."
            + getName());
    }
    
  4. Class类中有个getField()方法,一般通过class对象调用该方法,指定字段名称,就会返回该class对象描述的目标类中指定成员的Field实例。如果指定的字段名称在对应的目标类中不存在,则抛出异常。
  5. 测试:
    ```java package zeh.test.demo.com; /**
    • 功能描述 *
    • @since 2020-06-24 */ public class Test { public static void main(String args[]) throws NoSuchFieldException { System.out.println(Person.class.getField(“name”)); System.out.println(Integer.class.getField(“TYPE”)); System.out.println(Person.class.getField(“CAO”)); } } ```
  6. 测试结果:
    public java.lang.String zeh.test.demo.com.Person.name
    public static final java.lang.Class java.lang.Integer.TYPE
    Exception in thread "main" java.lang.NoSuchFieldException: CAO
        at java.lang.Class.getField(Class.java:1703)
        at zeh.test.demo.com.Test.main(Test.java:14)
    Process finished with exit code 1
    

2.4 Field对象获取指定对象上的当前字段值

  1. Field类用来描述一个类里面的属性成员信息的。
  2. Field提供了一个方法:
    public Object get(Object obj) throws IllegalArgumentException,IllegalAccessException;
    
  3. 返回值:对象obj中所表示的字段的值。
  4. 注意点:
    如果底层字段是一个静态字段,则忽略 obj 变量,直接传入一个null值即可。
    其他注意点参考Java API。
  5. 测试:
    package zeh.test.demo.com;
        
    public class Test {
        public static void main(String args[]) throws NoSuchFieldException, IllegalAccessException {
            System.out.println(Person.class.getField("name").get(new Person("Eric")));
            System.out.println("Integer.class TYPE is : " + Integer.class.getField("TYPE"));
            System.out.println(Integer.class.getField("TYPE").get(null));
        }
    }
    
  6. 测试结果:
    Eric
    Integer.class TYPE is : public static final java.lang.Class java.lang.Integer.TYPE
    int
    Process finished with exit code 0
    
  7. 结果分析:
    System.out.println(Person.class.getField("name").get(new Person("Eric")));
    

    上面语句输出了Eric,很好理解。因为Person.class描述的目标类是Person,所以getField(“name”)获取的Field对象就是描述Person类里面的name字段的。然后去获取指定实例对象new Person(“Eric”)中的name字段的值,即Eric。
    重点分析如下语句:

    System.out.println(Integer.class.getField("TYPE").get(null));
    

    Integer类是int的包装类,不是我们自定义的类。而且我们看到 Integer.class.getField(“TYPE”) 输出的是 public static final java.lang.Class java.lang.Integer.TYPE。
    说明了Integer内部有一个常量字段TYPE。
    我们查看Integer的源码:

    public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
    

    Class.getPrimitiveClass(“int”)方法是Class类的内部方法,用来获取基本类型的class对象,所以说:Integer内部的TYPE字段实际上就是JAVA虚拟机内置的int的class对象。
    即:Integer.TYPE == int.class。其他包装类类似。
    同时,因为TYPE字段是static的,所以 Integer.class.getField(“TYPE”).get(null) 直接传入了一个null值。
    很显然从TYPE字段的定义中就看到,Integer类一旦被JVM装载,TYPE字段就初始化为int的内置class对象了。
    现在得出结论了:Integer.class.getField(“TYPE”).get(null);最终返回的是Integer内部的TYPE字段的值,这个值实际上就是int基本类型的class对象。

2.5 isPrimitive识别包装类

  1. 回到刚开始的问题,isPrimitive 方法不能直接判断包装类,直接判断包装类将返回false。即意味着该方法认为包装类不是一个预创建的基本类型(很明显包装类的class对象确实不是9种预创建的class对象之一)。
  2. 但我们在业务中,认为基本类型和包装类实际上是代表相同的业务含义的,即我们认为:int和Integer的业务含义是完全相同的。
  3. 那么,如何通过 isPrimitive 方法处理包装类的类型认证呢?
  4. 猜想:
    既然 isPrimitive 方法只能判断JVM预创建的9种基本类型的class对象,那么要想使用 isPrimitive 去正确判断对应的包装类型,应该想办法从对应的包装类型中获取到与之相关联的预创建的class对象。
  5. 通过上面的测试已经有了方案:
    Integer.class.getField(“TYPE”).get(null);返回的是Integer内部的TYPE字段的值,实际上就是JVM预创建的int的class对象。
  6. 挨个查看8种包装类的TYPE字段的定义:
    Byte类中的TYPE字段public static final Class<<Byte> TYPE = (Class<Byte>) Class.getPrimitiveClass("byte");  
    Short类中的TYPE字段public static final Class<<Short> TYPE = (Class<Short>) Class.getPrimitiveClass("short");  
    Integer类中的TYPE字段public static final Class<<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
    Long类中的TYPE字段public static final Class<<Long> TYPE = (Class<Long>) Class.getPrimitiveClass("long");
    Float类中的TYPE字段public static final Class<<Float> TYPE = (Class<Float>) Class.getPrimitiveClass("float");
    Double类中的TYPE字段public static final Class<<Double> TYPE = (Class<Double>) Class.getPrimitiveClass("double");
    Boolean类中的TYPE字段public static final Class<<Boolean> TYPE = (Class<Boolean>) Class.getPrimitiveClass("boolean");
    Character类中的TYPE字段public static final Class<<Character> TYPE = (Class<Character>) Class.getPrimitiveClass("char");
    
  7. 我们全面验证下8种包装类型的TYPE字段取出来都是啥:
    package zeh.test.demo.com;
        
    public class Test {
        public static void main(String args[]) throws NoSuchFieldException, IllegalAccessException {
            System.out.println(Byte.class.getField("TYPE").get(null));
            System.out.println(Short.class.getField("TYPE").get(null));
            System.out.println(Integer.class.getField("TYPE").get(null));
            System.out.println(Long.class.getField("TYPE").get(null));
            System.out.println(Float.class.getField("TYPE").get(null));
            System.out.println(Double.class.getField("TYPE").get(null));
            System.out.println(Boolean.class.getField("TYPE").get(null));
            System.out.println(Character.class.getField("TYPE").get(null));
        }
    }
    
  8. 我们看到8种包装类型中,TYPE字段实际上都是public static final的,即本身就是常量,因此,我们可以直接通过包装类的类名进行访问:
    package zeh.test.demo.com;
        
    public class Test {
        public static void main(String args[]) {
            System.out.println(Byte.TYPE);
            System.out.println(Short.TYPE);
            System.out.println(Integer.TYPE);
            System.out.println(Long.TYPE);
            System.out.println(Float.TYPE);
            System.out.println(Double.TYPE);
            System.out.println(Boolean.TYPE);
            System.out.println(Character.TYPE);
        }
    }
    
  9. 上面两种方式的测试结果是一致的,如下:
    byte
    short
    int
    long
    float
    double
    boolean
    char
    Process finished with exit code 0
    

2.6 isPrimitive实现基本类型和对应包装类型的通用判断

  1. 基于上面的分析,我们知道,isPrimitive 去判断基本类型是很直观的,因为JVM为基本类型预创建的class对象,通过isPrimitive返回的就是true。
  2. isPrimitive 去判断包装类型,可以先获取到包装类型里面的TYPE字段的值,因为TYPE字段的值表示的就是包装类型对应的预创建的基本类型的class对象,比如Integer类里面的TYPE字段的值表示的就是JVM预创建的int类型的class对象。
  3. 获取包装类的TYPE字段的值有两种方式,以Integer举例:
    1 Integer.class.getField("TYPE").get(null);  
    2 Integer.TYPE;  
    
  4. 分析上述两种方式,很明显,第二种直接通过包装类获取TYPE的方式根本无法进行扩展。因为如果要实现一个通用的方法去专门实现包装类型的判断的话,我们无法从外部就直接传入一个包装类的TYPE。因为我们不知道要传入的对象是啥啊。而第一种方式,外部只需要传入一个class对象即可。
  5. 测试:
    package zeh.test.demo.com;
        
    public class Test {
        public static void main(String args[]) {
            System.out.println(isPrimitive(byte.class));
            System.out.println(isPrimitive(short.class));
            System.out.println(isPrimitive(int.class));
            System.out.println(isPrimitive(long.class));
            System.out.println(isPrimitive(float.class));
            System.out.println(isPrimitive(double.class));
            System.out.println(isPrimitive(boolean.class));
            System.out.println(isPrimitive(char.class));
            System.out.println(isPrimitive(Byte.class));
            System.out.println(isPrimitive(Short.class));
            System.out.println(isPrimitive(Integer.class));
            System.out.println(isPrimitive(Long.class));
            System.out.println(isPrimitive(Float.class));
            System.out.println(isPrimitive(Double.class));
            System.out.println(isPrimitive(Boolean.class));
            System.out.println(isPrimitive(Character.class));
            System.out.println(isPrimitive(Object.class));
            System.out.println(isPrimitive(Person.class));
        }
         
        // 判断目标clazz对象是否是基本类型或其对应的包装类型
        // clazz 目标clazz对象
        // true:是基本类型 false:不是基本类型
        public static boolean isPrimitive(Class clazz) {
            return clazz.isPrimitive() || isWrapPrimitive(clazz);
        }
        
        // 判断目标clazz对象是否是包装类型
        // clazz 目标clazz对象
        // true:是包装类型 false:不是包装类型
        public static boolean isWrapPrimitive(Class clazz) {
            try {
                return ((Class) (clazz.getField("TYPE").get(null))).isPrimitive();
            } catch (Exception e) {
                return false;
            }
        }
    }
    
  6. 测试结果:
    true
    true
    true
    true
    true
    true
    true
    true
    true
    true
    true
    true
    true
    true
    true
    true
    false
    false
    Process finished with exit code 0
    

3. 跟本地类、匿名类等相关的api测试

3.1 修改上述案例中的ClassD如下:

package zeh.test.demo.com.class1;

// ClassD内部定义了各种内部类
public class ClassD extends ClassC {
    private static String staticStr = "外部类的静态成员";
    private String privateStr = "外部类的private成员";
    private String str = "外部类的成员";
    @Override
    public void abstractFun() {
        System.out.println("ClassD implements");
    }
    public void testD() {
        System.out.println("ClassD testD");
    }
 
    // 虽然内部类可以无条件的访问外部类的成员和方法,但是外部类想要访问内部类的成员和方法,就需要明确创建内部类的对象了。
    public void doInnerField() {
        // 外部类中必须先常见成员内部类的对象才能访问内部类的成员和方法
        new Inter().testInner();
        System.out.println(new Inter().str);
    }
    /**
     * 定义一个成员内部类:成员内部类可以无条件的访问自己的外部类中测成员属性和成员方法(包括静态成员和private成员)
     */
    class Inter {
        private String str = "内部类的private成员,和外部类的private成员同名,将对外部类的private成员进行覆盖";

        // 内部类定义了和外部类的同名方法,也将对外部类的同名方法进行覆盖。
        public void testD() {
            System.out.println("Inter testD");
        }
        public void testInner() {
            System.out.println("测试成员内部类,staticStr = " + staticStr + ";privateStr = " + privateStr);
            // 因为成员内部类对外部类的同名属性和方法进行了覆盖,所以如果直接访问的话访问的将是内部类覆盖后的成员和方法。
            System.out.println("访问str成员,实际上访问的就是内部类自己覆盖后的str:" + str);
            // 直接执行testD方法也是一样
            testD();
            // 如果想继续访问外部类的成员和方法,则必须实例化一个ClassD外部类的对象;或者直接使用 "类名.this.成员" 的方式显式指定当前的this是内部类还是外部类的。
            // 方式一:直接创建外部类对象。
            new ClassD().testD();
            System.out.println(new ClassD().str);
            // 方式二:通过“外部类.this.成员”的方式指定this代表的是外部类的this。如果直接使用this肯定是当前类的this。
            // 说明:“类名.this.成员”或者“类名.super.成员”这种方式只有在存在内部类外部类的时候才有这种语法。
            ClassD.this.testD();
            System.out.println(ClassD.this.str);
        }
    }
 
    // 定义一个方法,专门访问局部内部类
    public void testFunInner() {
        // 定义一个局部内部类,局部内部类是定义在方法或者某个作用域里面的,就像局部变量一样,是不能有任何访问权限修饰和static修饰的。
        // 局部内部类也称为本地类。
        // 局部内部类也可以无条件的访问外部类中的成员和方法。但是外部类可不能随意的访问局部内部类,只能在当前方法中才能访问局部内部类。
        // 局部内部类和成员内部类的区别在于,局部内部类只能在当前的作用域或者当前的方法中进行访问。
        class FunInner {
            private String funStr = "局部内部类的成员";
            private String str = "局部内部类的private成员,和外部类的private成员同名,将对外部类的private成员进行覆盖";
            public void testFunInner() {
                System.out.println("局部内部类的方法,staticStr = " + staticStr + ";privateStr = " + privateStr);
                System.out.println("访问str成员,实际上访问的就是局部内部类自己覆盖后的str:" + str);
                // 明确指定要访问外部类的同名成员才可
                System.out.println(ClassD.this.str);
            }
        }
        // 只能在当前域中访问局部内部类,同理,需要创建局部内部类的对象才能访问局部内部类
        FunInner funInner = new FunInner();
        funInner.testFunInner();
        System.out.println(new FunInner().funStr);
        // 判断 FunInner 是否是本地类
        System.out.println("FunInner 是否是本地类?" + FunInner.class.isLocalClass());
    }

    // 定义一个方法,专门实现匿名内部类。
    // 匿名内部类一般用于实现接口,通过匿名实现接口进行回调逻辑的指定。
    public MyInterface testNiMingInner() {
        // 没有定义类名,直接创建某个类的对象,并实现某个类中的方法。
        // 匿名内部类的实现必须具备父类或者接口的前提,才有匿名内部类的实现。
        // 匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。
        // 匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
        MyInterface myInterface = new MyInterface() {
            @Override
            public void abstractFun() {
                System.out.println("匿名内部类实现的abstractFun");
            }
            @Override
            public void eat() {
                System.out.println("匿名内部类实现的eat");
            }
            @Override
            public void drink() {
                System.out.println("匿名内部类实现的drink");
            }
            @Override
            public void sleep() {
                System.out.println("匿名内部类实现的sleep");
            }
        };
        myInterface.drink();
        myInterface.abstractFun();
        myInterface.eat();
        myInterface.sleep();
        // 返回该匿名类对象
        return myInterface;
    }
    
    // 定义一个静态内部类,静态内部类就好像一个类的静态成员。
    // 静态内部类不能访问外部类的非静态成员,如果要访问,必须明确创建外部类的对象。
    static class StaticInner {
        private String str = "静态内部类中的成员";
        public void testStaticInner() {
            System.out.println("测试成员内部类,staticStr = " + staticStr + ";privateStr = " + new ClassD().privateStr);
            System.out.println(str);
        }
    }
}

文档信息

Search

    Table of Contents