首页 » java » 正文

Java程序设计进阶复习笔记(续)

  1. java虚拟机有自己想象的硬件,如处理器、堆栈、寄存器、还有相应的指令系统。

  2. 每个java程序单独的运行一个java虚拟机。

  3. 使用java命令就是启动java虚拟机。

  4. java虚拟机对执行的方法从main()开始执行。

  5. 类加载子系统:加载程序中的类型(类class和接口interface)

  6. 执行引擎:负责执行被加载类中包含的指令。

  7. 内存包含:方法区、堆、虚拟机栈、本地方法栈、程序计数器。

  8. 方法区和堆属于线程共享;虚拟机栈、本地方法栈、程序计数器属于线程私有。

  9. 本地方法栈是c和c++语言执行的栈,出错同样会抛出异常。

  10. 方法区存储类信息、常量、静态变量、class文件,方法区也会进行垃圾回收,常量池的清理和类型卸载等等。

  11. 类型的连接和加载是在程序运行的过程中执行的。

  12. 类的生命周期分为:加载、验证、准备、解析、初始化、使用、卸载。

  13. 加载的过程
    (1). 通过全名获取类的二进制字节码。
    (2). 将字节码的结构转化为方法区的运行时数据结构
    (3). 在java堆中生成一个代表这个类的class对象,作为方法区这些数据的访问入口。

  14. 验证
    (1). 虚拟机规范:是否符合class文件的存储格式
    (2). 文件格式验证:是否为class文件格式的规范,并能被当前版本虚拟机处理
    (3). 元数据进行语义校验,保证不存在不符合java语言规范的元数据信息
    (4). 字节码验证:对类的方法体进行验证,保证方法体在运行时不会做出危害虚拟机的行为。
    (5). 符号引用

  15. 准备:为类变量分配内存并进行初始化。

  16. 解析:将类的字段、方法、类、接口等的符号替换成直接引用的过程。

  17. 初始化:clinit方法:编译器自动的收集类变量的赋值语句和静态语句块中的语句合并产生。
    虚拟机会保证任何一个类的clinit方法被正确的加锁和同步。

  18. 虚拟机初始化类时,若父类未进行初始化,会进行父类的初始化。

  19. 虚拟机启动的时候,会主动初始化带main方法的类。

  20. 主动引用和被动引用不是很懂。

  21. 垃圾标记算法
    (1). 引用计数算法:无法解决相互引用的问题
    (2). 根搜索算法:设置一些GCroot,如果一个对象由GCroot不可达,则说明该对象是可以回收的。

  22. 可以设为GCroot的对象
    (1). 虚拟机栈中引用的对象
    (2). 方法区中的类静态属性引用的对象。
    (3). 方法区中的常量引用的对象。
    (4). 本地方法栈中方引用的对象。

  23. 引用方式有强引用、软引用、弱引用、虚引用。

  24. 软引用

public class Test {
    public static void main(String args[]){
        Object obj = new Object();
        SoftReference<Object> sf = new SoftReference<>(obj);  
        obj = null;  
        sf.get();  
    }
}

这里是sf对对象有个软引用。软引用主要实现类似缓存的功能,内存足够时,直接使用软引用查询数据,而当内存不足时,才从真实的来源查询数据。

  1. 弱引用,在第二次垃圾回收时才进行垃圾回收,用来判断对象是不是快要删除了。
public class Test {
    public static void main(String args[]){
        Object obj = new Object();
        WeakReference<Object> wf = new WeakReference<>(obj);
        obj = null;
        wf.get();
        wf.isEnqueued();  //返回对象是否被垃圾回收器回收
    }
}
  1. 虚引用,主要用来检测对象是否被回收了
public class Test {
    public static void main(String args[]){
        Object obj = new Object();
        PhantomReference<Object>  pf = new PhantomReference<Object>(obj, new ReferenceQueue<Object>());
        obj=null;
        pf.get();  //永远返回null
        pf.isEnqueued();
    }
}
  1. System.gc会影响系统的性能。

  2. 年轻代:新创建的对象都放在这里,因为大多数的对象很快变得不可达,所以大多数的对象在年轻代中创建,然后消失。当对象从这块区域小时,我们称为发生了一次”minor GC”
    老年代:没有变得不可达,存活下来的年轻代对象放到这里。内存区域一般比年轻代大,Gc的次数比年轻代少,当老年代的对象回收时,我们称为发生了”Full GC”

  3. 年轻代有一块Eden区和两块Survivor区,经过重复多次的垃圾回收后的对象被移到老年代区。

  4. 垃圾清除算法:
    (1). mark-sweep算法(标记-清除算法),会有很多的碎片,当需要大的块时,就会触发垃圾回收。
    (2). Copying算法,分成大小相等的两块,一块用完了,把所有存活的放在另一块上,将当前块进行清除,可用内存缩减到原来的一半。如果存活的对象很多,效率将很低。
    (3). Mark-Compact算法(标记-整理算法),将存活的对象往一边进行整理。
    (4). 分代收集算法:分为新生代、老年代和永久代。新生代分为一个Eden和两个Survivor,使用的Coping算法;老年代使用的Mark-compact算法;永久代主要回收废弃的常量和无用的类。

  5. 典型的垃圾收集器
    (1). Serial/Serial Old,单线程的收集器,收集垃圾时,必须停止所有的用户线程,新生代采用coping,老年代采用Mark-Compact算法,简单高效,但是会停顿。
    (2). ParNew:Serial的多线程版本
    (3). parallel Scavenge收集器:新生代的多线程收集器,采用Copying算法,主要是为了达到可控的吞吐量,Parallel old,老年代的版本mark-compact算法,其他线程不需要等待。
    (4). CMS收集器(Current mark sweep):以获取最短的回收停顿时间为目标的收集器,mark-sweep算法。
    (5). G1收集器:最前沿的成果,面想服务端的收集器,利用多CPU、多核环境,并行与并发的收集器。

  6. RTTI:运行时类型识别

  7. Class类
    (1). 每装载一个类时,就会创建一个Class实例,这个实例就代表Class类型,通过实例获取类型信息。
    (2). Method[] getMethods()
    (3). Field[] getFields()
    (4). Constructor<?>[] getDeclaredConstructors()

  8. 通过Class得到实例:newInstance()方法,注意强制类型转换

public class Main{
    public static void main(String args[]) throws Exception{
        Method mth = Main.class.getMethod("add", new Class[]{Integer.class, Integer.class});
        mth.invoke(Main.class.newInstance(), new Integer(1), new Integer(2));
        Method mth1 = Main.class.getMethod("stringAdd", new Class[]{String.class});
        mth1.invoke(Main.class.newInstance(), "--test");
    }
    public static void add(Integer param1, Integer param2){
        System.out.println(param1.intValue()+param2.intValue());
    }
    public static void stringAdd(String abc){
        System.out.println("out"+abc);
    }
}
  1. 静态代理
    代理模式一般涉及到的角色有:
    抽象角色:声明真实对象和代理对象的共同接口,对应代理接口(Subject);
    真实角色:代理角色所代表的真实对象,是我们最终要引用的对象,对应委托类(RealSubject);
    代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装,对应代理类(ProxySubject)
abstract class Subject{
    public abstract void request();
}

class RealSubject extends Subject{
    @Override
    public void request(){
        System.out.println("from real request...");
    }
}

class ProxySubject extends Subject{
    private RealSubject realSubject;
    public void request(){
        preRequest();
        if(null == realSubject){
            realSubject = new RealSubject();
        }
        realSubject.request();
        postRequest();
    }
    public void preRequest(){
        System.out.println("pre request...");
    }
    public void postRequest(){
        System.out.println("post reqquest...");
    }
}
public class Main{
    public static void main(String args[]){
        Subject subject = new ProxySubject();
        subject.request();
    }
}
  1. 动态代理器,java.lang.reflect.Proxy.
//该方法用于获取指定对象的调用处理器
static InvocationHandler getInvocationHandler(Object proxy);
//用于获取关联指定类装载器和一组接口的动态代理类的类对象
static Class getProxy(ClassLoader loader, Class[] interfaces);
//用于判断指定类是不是一个动态代理类
static boolean isProxyClass(Class cl);
//利用指定类装载器、一组接口及调用处理器生成动态代理实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h);

Object invoke(Object proxy, Method method, Object[] args);
第一个代理类实例,第二个参数被调用的方法对象,第三个调用的参数。

interface Subject{
    public void request();
}

class RealSubject implements Subject{
    public RealSubject(){}
    public void request(){
        System.out.println("From real subject...");
    }
}

class DynamicSubject implements InvocationHandler{
    private Object sub;
    public DynamicSubject(){}
    public DynamicSubject(Object obj){
        this.sub=obj;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        System.out.println("before calling "+method);
        method.invoke(sub, args);
        System.out.println("after calling "+method);
        return null;
    }
}

public class Main{
    public static void main(String args[]){
        RealSubject real = new RealSubject();  //创建真实对象
        InvocationHandler ih = new DynamicSubject(real);//用真实对象创建调用处理器
        Class cls = ih.getClass();  //得到调用处理器的类
        //通过调用处理器的类得到类加载器、接口,再加上本身来创建代理类
        Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),ih);
        subject.request();  
    }
}

  1. 动态代理
    优点:声明的所有方法都被转移到调用处理器的一个集中的方法中进行处理。
    缺点:无法摆脱仅支持interface。

  2. jvm自带的默认加载器
    根类加载器:c++编写而成
    扩展类加载器:java编写
    系统类、应用类加载器:java编写
    用户自定义:java.lang.ClassLoader的子类,用户可以自己定制类的加载方式。

  3. 类的加载方式
    (1). 本地编译好的文件直接加载
    (2). 网络加载,java.net.URLClassLoader可以加载指定的类。
    (3). jar、zip文件加载类
    (4). java文件编译成class文件进行加载

发表评论