美文网首页
Thinking in Java 第14章 类型信息

Thinking in Java 第14章 类型信息

作者: KuTear | 来源:发表于2016-10-07 16:06 被阅读59次

    date: 2016-09-03 12:07
    status: public
    tags:[Thinking In Java]
    title: 'Thinking in Java 第14章 类型信息'


    本文发表于KuTear's Blog,转载请注明

    Class

    构造器/Static块初始化顺序

    单一类的情况(没有继承)

    对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序依次是(静态变量、静态初始化块)>(变量、初始化块)>构造器。

    public class InitialOrderTest {
            /* 静态变量 */
        public static String staticField = "静态变量";
            /* 变量 */
        public String field = "变量";
            /* 静态初始化块 */
        static {
            System.out.println( staticField );
            System.out.println( "静态初始化块" );
        }
            /* 初始化块 */
        {
            System.out.println( field );
            System.out.println( "初始化块" );
        }
            /* 构造器 */
        public InitialOrderTest(){
            System.out.println( "构造器" );
        }
    
    
        public static void main( String[] args ){
            new InitialOrderTest();
        }
    }
    

    输出

    静态变量
    静态初始化块
    变量
    初始化块
    构造器

    包含继承关系的时候

    static class Parent {
            /* 静态变量 */
            public static String p_StaticField = "父类--静态变量";
            /* 变量 */
            public String    p_Field = "父类--变量";
            protected int    i    = 9;
            protected int    j    = 0;
            /* 静态初始化块 */
            static {
                System.out.println( p_StaticField );
                System.out.println( "父类--静态初始化块" );
            }
            /* 初始化块 */
            {
                System.out.println( p_Field );
                System.out.println( "父类--初始化块" );
            }
            /* 构造器 */
            public Parent(){
                System.out.println( "父类--构造器" );
                System.out.println( "i=" + i + ", j=" + j );
                j = 20;
            }
        }
    
        public static class SubClass extends Parent {
            /* 静态变量 */
            public static String s_StaticField = "子类--静态变量";
            /* 变量 */
            public String s_Field = "子类--变量";
            /* 静态初始化块 */
            static {
                System.out.println( s_StaticField );
                System.out.println( "子类--静态初始化块" );
            }
            /* 初始化块 */
            {
                System.out.println( s_Field );
                System.out.println( "子类--初始化块" );
            }
            /* 构造器 */
            public SubClass(){
                System.out.println( "子类--构造器" );
                System.out.println( "i=" + i + ",j=" + j );
            }
        }
    

    现在实例化一个SubClass将得到以下的输出

    父类--静态变量
    父类--静态初始化块
    子类--静态变量
    子类--静态初始化块
    父类--变量
    父类--初始化块
    父类--构造器
    i=9, j=0
    子类--变量
    子类--初始化块
    子类--构造器
    i=9,j=20

    由此我们知道父类构造器先于子类构造器调用,当在父类构造器中添加钩子函数时要注意,有可能子类实现的钩子函数中用到的参数没有被初始化。

    abstract class Parent{
      public Parent(){
        hook();
      }
      public abstract void hook();
    }
    
    class Sub extends Parent{
      private SomeVar mVar;
      public Sub(){
         mVar = new SomeVar(); 
      }
      public void hook(){
        mVar.doSomeThing();  //当调用时 mVar==null
      }
    }
    

    常用方法

    常用方法 方法说明
    isInterface() 判断是否在接口
    getInterfaces() 获取class实现的所有接口
    getSuperclass() 获取class的直接父类
    newInstance() 实例化对象,必须含有默认构造器
    getPrimitiveClass(String) 获取原始类型对应的Class(Integer.TYPE等的实现)
    cast(Object) 转化Object为具体的类型(Class< T > 中的T)
    isAssignableFrom(Class<?>) 判断传入的class是否为当前class的子类或就是当前类
    getModifiers() 返回当前class的修饰符(public/private/protect等等)
    getMethod(String, Class<?>...) 根据方法名字参数类型获取方法

    类字面常量

    使用类必须要准备三个步骤:

    1. 加载,这是由类加载器执行的。该步骤还将查找字节码,并从这些字节码中创建一个Class对象;
    2. 链接。在链接阶段将验证类中的字节码,为静态域分配存储空间,并且如果必须的话,将解析这个类的创建的对其他类的所有引用。
    3. 初始化,如果该类有超类,则对其初始化,执行静态初始化器和静态初始化块。

    字面常量就是ClassName.class的形式。在Android启动Activity的时候经常都有使用。

    类加载

    Class clazz1 = OneClass.class
    Class clazz2 = Class.forName("OnClass");
    

    上面的代码中,clazz1不会加载OneClassJVM,然而clazz2回加载。
    初始化被延迟到了对静态方法(构造器也是隐式静态方法)或非常数静态域进行首次引用才执行。

    clas Initable{
     public final static int staticFinal = 1; //常数静态域,不会加载class
     public final static double staticFinal2 = Math.random();
     public static double staticVar = 1; 
    }
    

    动态代理

        public static void main(String[] args) {
            A a = new A();
            IA proxyA = (IA) Proxy.newProxyInstance(a.getClass().getClassLoader(),new Class[]{
                IA.class
            },new ProxyA(a));
            proxyA.doSomeThing();
        }
    
        public static interface IA{
            public void doSomeThing();
        }
    
        public static class A implements IA{
            @Override
            public void doSomeThing(){
                System.out.println("do some thing");
            }
        }
    
        public static class ProxyA implements InvocationHandler{
    
            private Object real;
            public ProxyA(Object real) {
                this.real = real;
            }
    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                System.out.println("before do some thing");
                return method.invoke(real,args);
            }
        }
    

    输出

    before do some thing
    do some thing

    通过使用代理,我们可以在函数调用前执行一些操作。

    QA

    判断一个Class是否为抽象类/接口?

    判断接口

      Class a = IA.class;
      System.out.println(a.isInterface());
    

    判断抽象类

      Class a = AbstractA.class;
      int modifier =  a.getModifiers(); //获取修饰符
      System.out.println(Modifier.isAbstract(modifier));
    

    一个class被加载到JVM后会不会再重JVM中移除?

    这个问题是看书过程中想到的,查阅资料才发现需要深入了解JVM,这里只是初步说明。
    关于java虚拟机规范中时如何阐述类型卸载(unloading)的:

    A class or interface may be unloaded if and only if its class loader is unreachable. The bootstrap class loader is always reachable; as a result, system classes may never be unloaded.

    再看一下Java语言规范提供的关于类型卸载的更详细的信息(部分摘录):

    1. An implementation of the Java programming language may unload classes.
    2. Class unloading is an optimization that helps reduce memory use. Obviously,the semantics of a program should not depend on whether and how a system chooses to implement an optimization such as class unloading.
    3. Consequently,whether a class or interface has been unloaded or not should be transparent to a program

    通过以上我们可以得出结论: 类型卸载(unloading)仅仅是作为一种减少内存使用的性能优化措施存在的,具体和虚拟机实现有关,对开发者来说是透明的

    instaceof原理

    instaceof表示“你是这个类吗,或者是它的子类吗”
    ==则仅仅表示“你是这个类吗”
    深入了解请看RednaxelaFX的知乎回答

    参考

    相关文章

      网友评论

          本文标题:Thinking in Java 第14章 类型信息

          本文链接:https://www.haomeiwen.com/subject/lkvyyttx.html