2018年7月

  1. 回收作用
    垃圾回收可以有效的防止内存泄露,有效的使用空闲的内存
    (内存泄露:指该内存空间使用完毕后未回收,在不涉及复杂数据结构的一般情况下,java的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度)
    如果不进行垃圾回收,内存迟早都会被消耗空
  2. 回收对象
    那些不可能再被任何途径使用的对象
  3. 垃圾回收实现原则
  4. 发现无用的信息对象;
  5. 回收将无用对象占用的内存空间。使该空间可被程序再次使用。
  6. 如何确定一个对象是否可以被回收
  7. 引用计数法--无法处理循环引用的问题
    原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。
    垃圾回收时,只用收集计数为 0 的对象。
  8. 可达性分析算法:判断对象的引用链是否可达
  9. 垃圾收集算法
  10. 标记清除算法--需要暂停整个应用,同时,会产生内存碎片
    第一阶段从引用根节点开始标记所有被引用的对象
    第二阶段遍历整个堆,把未标记的对象清除
  11. 复制算法--需要两倍内存空间
    此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。
    垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中;
  12. 标记整理算法
    第一阶段从根节点开始标记所有被引用对象
    第二阶段遍历整个堆,把清除未标记对象并且把存活对象 “压缩” 到堆的其中一块,按顺序排放
  13. 分代收集算法
  14. 回收算法的直接应用--垃圾收集器

ref:
《深入java虚拟机》
图解Java 垃圾回收机制

结构型模式:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式

    1. 代理模式(Proxy)
代理模式就是多一个代理类出来,替原对象进行一些操作
为其他对象提供一个代理以控制对某个对象的访问
使用代理模式,可以将功能划分的更加清晰,有助于后期维护
比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面
希望找一个更熟悉的人去帮你做,此处的代理就是这个意思
public interface Sourceable {  
    public void method();  
}  
public class Source implements Sourceable {  
  
    @Override  
    public void method() {  
        System.out.println("the original method!");  
    }  
} 
public class Proxy implements Sourceable {  
    private Source source;  
    public Proxy(){  
        super();  
        this.source = new Source();  
    }  
    @Override  
    public void method() {  
        before();  
        source.method();  
        atfer();  
    }  
    private void atfer() {  
        System.out.println("after proxy!");  
    }  
    private void before() {  
        System.out.println("before proxy!");  
    }  
}  
public class ProxyTest {  
    public static void main(String[] args) {  
        Sourceable source = new Proxy();  
        source.method();  
    }  
}
//输出
before proxy!
the original method!
after proxy!
在Proxy类中不仅执行了委托类Source 的method方法还增加了两个功能
  • 静态代理

    上面例子就是静态代理--
1.为了保持行为的一致性,代理类和委托类通常会实现相同的接口
2.代理类和委托类实现了相同的接口,这样就出现了大量的代码重复
3.代理对象只服务于一种类型(如上的Sourceable )的对象,如果要服务多类型的对象 程序开发中必然会产生许多的代理类
  • 动态代理

    动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象
    在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 类的支持
public interface UserManager {
        public void addUser(String userId, String userName);
        public void delUser(String userId);
        public String findUser(String userId);
        public void modifyUser(String userId, String userName);
}
public class UserManagerImpl implements UserManager {
    @Override
    public void addUser(String userId, String userName) {
        System.out.println("UserManagerImpl.addUser");
    }
    @Override
    public void delUser(String userId) {
        System.out.println("UserManagerImpl.delUser");
    }
    @Override
    public String findUser(String userId) {
        System.out.println("UserManagerImpl.findUser");
        return "张三";
    }
    @Override
    public void modifyUser(String userId, String userName) {
        System.out.println("UserManagerImpl.modifyUser");
    }
}
//动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke方法。
//该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法返回的值是被代理接口的一个实现类
public class LogHandler implements InvocationHandler {
    // 目标对象
    private Object targetObject;
    //绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。            
    public Object newProxyInstance(Object targetObject){
        this.targetObject=targetObject;
        //该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例  
        //第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
        //第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
        //第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
        //根据传入的目标返回一个代理对象
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                targetObject.getClass().getInterfaces(),this);
    }
    @Override
    //关联的这个实现类的方法被调用时将被执行
    /*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("start-->>");
        for(int i=0;i<args.length;i++){
            System.out.println(args[i]);
        }
        Object ret=null;
        try{
            /*原对象方法调用前处理日志信息*/
            System.out.println("satrt-->>");
            //-----------调用目标方法-------------
            ret=method.invoke(targetObject, args);
            /*原对象方法调用后处理日志信息*/
            System.out.println("success-->>");
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("error-->>");
            throw e;
        }
        return ret;
    }
}
public class Client {
    public static void main(String[] args){
        LogHandler logHandler=new LogHandler();
        UserManager userManager=(UserManager)logHandler.newProxyInstance(new UserManagerImpl());
        //UserManager userManager=new UserManagerImpl();
        userManager.addUser("1111", "张三");
    }
}
//结果
start-->>
1111
张三
satrt-->>
UserManagerImpl.addUser
success-->>

ref:
23种设计模式全解析
JAVA学习篇--静态代理VS动态代理

问题解决+经验复用
设计模式原则:对扩展开放,对修改关闭,使用继承、抽象类、接口、低耦合

创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。

  • 1.单例

    确保一个类只有一个实例,并提供该实例的全局访问。
public class Singleton {
    private volatile static Singleton uniqueInstance;
    private Singleton() {
    }
    public static Singleton getUniqueInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}
//双重校验锁先判断 uniqueInstance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。
uniqueInstance = new Singleton(); 
1,分配内存空间
2,初始化对象
3,将 uniqueInstance 指向分配的内存地址
//由于 JVM 具有指令重排的特性,有可能执行顺序变为了 1>3>2.使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
  • 2.工厂模式--类的创建依赖工厂类
  • 2.1简单工厂方法

    在创建一个对象时不向客户暴露内部细节,并提供一个实现的通用接口。
public interface Sender {
    public void Send();
}
public class MailSender implements Sender {
    @Override
    public void Send() {
        System.out.println("this is mailsender!");
    }
}
public class SmsSender implements Sender {
    @Override
    public void Send() {
        System.out.println("this is sms sender!");
    }
}
public class SendFactory {
    public Sender produce(String type) {
        if ("mail".equals(type)) {
            return new MailSender();
        } else if ("sms".equals(type)) {
            return new SmSender();
        } else {
            System.out.println("请输入正确的类型!");
            return null;
        }
    }
}
public class FactoryTest {
    public static void main(String[] args) {
        SendFactory sendFactory = new SendFactory();
        Sender sender = sendFactory.produce("mail");
        sender.send();
    }
}
在main方法中通过指定参数调用工厂方法生成与参数对应的对象
  • 2.2多个工厂方法

    在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象
public class SendFactory {
    public Sender produceMail(){
        return new MailSender();
    }
    public Sender produceSms(){
        return new SmsSender();
    }
}    
public class FactoryTest {
    public static void main(String[] args) {
        SendFactory factory = new SendFactory();
        Sender sender = factory.produceMail();
        sender.Send();
    }
}
  • 2.3静态工厂方法--方便

    不需要创建实例,直接调用即可
public class SendFactory {
    
    public static Sender produceMail(){
        return new MailSender();
    }
    
    public static Sender produceSms(){
        return new SmsSender();
    }
}
public class FactoryTest {
    public static void main(String[] args) {    
        Sender sender = SendFactory.produceMail();
        sender.Send();
    }
}
  • 2.4抽象工厂

    避免对工厂类的直接修改,抽象工厂增加新的功能只需增加一个新的工厂类,而不需要修改原来的代码
public interface Sender {
    public void Send();
}
public class MailSender implements Sender {
    @Override
    public void Send() {
        System.out.println("this is mailsender!");
    }
}
public class SmsSender implements Sender {
    @Override
    public void Send() {
        System.out.println("this is sms sender!");
    }
}

程序只有一个发送的功能,SmsSender、MailSender两个端为功能的实现;现在需要再加一个新建对象的功能

public interface Provider {
    public Sender produce();
}
public class SendMailFactory implements Provider  implements Sender {
    @Override
    public void Send() {
        System.out.println("this is mailsender!");
    }
    @Override
    public Sender produce(){
        return new MailSender();
    }
}
public class SendSmsFactory implements Provider  implements Sender{
 
    @Override
    public Sender produce() {
        return new SmsSender();
    }
        @Override
    public void Send() {
        System.out.println("this is sms sender!");
    }
}
  • 3.建造者模式

    建造者模式就是前面抽象工厂模式和最后的Test结合起来,将很多功能集成到一个类里,这个类可以创造出比较复杂的东西
public interface Sender {
    public void Send();
}
public class MailSender implements Sender {
    @Override
    public void Send() {
        System.out.println("this is mailsender!");
    }
}
public class SmsSender implements Sender {
    @Override
    public void Send() {
        System.out.println("this is sms sender!");
    }
}
public class Builder {
    private List<Sender> list = new ArrayList<Sender>();
    public void produceMailSender(int count){
        for(int i=0; i<count; i++){
            list.add(new MailSender());
        }
    }
    public void produceSmsSender(int count){
        for(int i=0; i<count; i++){
            list.add(new SmsSender());
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Builder builder = new Builder();
        builder.produceMailSender(10);
    }
}
  • 4.原型模式

    将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象
public class Prototype implements Cloneable {
    public Object clone() throws CloneNotSupportedException {
        Prototype proto = (Prototype) super.clone();
        return proto;
    }
}

浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。

public class Prototype implements Cloneable, Serializable {
    private static final long serialVersionUID = 1L;
    private String string;
    private SerializableObject obj;
    /* 浅复制 */
    public Object clone() throws CloneNotSupportedException {
        Prototype proto = (Prototype) super.clone();
        return proto;
    }
    /* 深复制 */
    public Object deepClone() throws IOException, ClassNotFoundException {
        /* 写入当前对象的二进制流 */
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        /* 读出二进制流产生的新对象 */
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }
    public String getString() {
        return string;
    }
    public void setString(String string) {
        this.string = string;
    }
    public SerializableObject getObj() {
        return obj;
    }
    public void setObj(SerializableObject obj) {
        this.obj = obj;
    }
}
class SerializableObject implements Serializable {
    private static final long serialVersionUID = 1L;
}

线程之前笔记

  1. 线程是操作系统能够进行运算调度的最小单位,是进程中的实际运作单位
  2. 线程和进程的区别

    1.每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销
    2.线程可以当成轻量级的进程,每个线程有独立的工作内存,切换开销小
    3.多进程:在操作系统中同时运行多个程序
    4.多线程:在同一程序中有多个顺序流(功能)同时执行(多核cpu)
    5.单线程:同一时间点只能有一个进程在执行(采用时间片轮转,让多个线程轮流执行。因为cpu执行速度快--假多线程)
  3. 线程理解

    每个分支都叫一个线程,main叫主分支(主线程)
    机器上的一个.class文件,机器上的一个.exe文件,这个叫做一个进程。程序的执行过程都是这样的:首先把程序的代码放到内存的代码区里面
    代码放到代码区后并没有马上开始执行,但这时候说明了一个进程准备开始,进程已经产生了,但还没有开始执行,这就是进程,所以进程其实是一个静态的概念,它本身就不能动
    平常所说的进程的执行指的是进程里面主线程开始执行了,也就是main()方法开始执行了。进程是一个静态的概念,在我们机器里面实际上运行的都是线程

    程序的执行流程:
    1.操作系统将分块的程序运输到内存对应的区间,过程中建立一个对应的表方便寻址
    2.当程序跑起来后根据局部性规律(当调用一块代码后,周围的代码也会被调用),一点点的调入内存中
    3.寻址过程中使用虚拟地址分配,就可以运行那些占较大内存的程序
  4. java中的线程

    java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,
    由于线程类本身就是调用的Runnable接口
    所以你可以继承java.lang.Thread 类或者直接调用Runnable接口(推荐)来重写run()方法实现线程
  5. 线程的创建和启动
  6. 继承Thread并重写run方法

    class MyThread extends Thread {
     public void run(){
    //线程体
     }
    }
  7. 实现Runnable接口

    //定义一个线程类
    public class ThreadTest implements Runnable{
     public void run() {
         // TODO Auto-generated method stub
                 线程体
     }
    }
    public class TestThread1{
     public static void main(String args[]){
         ThreadTest tt = new ThreadTest();
         tt.run();//就会执行run()方法再执行main方法
         Thread t = new Thread(tt);//启动一个新的线程需要Thread对象
         t.start();//启动新线程与主线程一起执行(交替执行)
         for(int i=0;i<10;i++){
             System.out.println("maintheod:"+i);
         }
     }
    }
    public static Thread currentThread()//获取当前线程的引用
  8. start与run

    start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,
    当调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程
  9. 线程状态转移

    阻塞状态(等待状态)
  • 创建一个新线程start准备好后进入就绪态(标上优先级别),进入cpu执行(线程调度器决定哪个线程进入运行态)
  • 时间片用完线程执行完后线程终止/遇到死锁后kill掉/时间片用完没有执行完的线程进入等待态
  1. 线程使用

    sleep(毫秒):休眠当前线程,进入等待状态,当休眠时间到解除等待状态,进入就绪状态等待cpu运行(睡眠时不会释放锁)
    yield()让出cpu给其他线程
    join()合并当前线程,让线程按照当前代码写的顺序执行
    wait():与synchronized关键字一起使用,进程进入等待状态,当notify/notifyall调用后解除等待态,wait调用后会释放锁,所以拿到锁后才可以运行
    setPriority()设置线程优先级别
  2. 线程同步

    对象互斥锁(synchronized)--保证共享数据操作的完整,这时就只有一个线程可以访问该对象/方法
    synchronized(this) {
     共享(同步)数据块
    }
    synchronized public void xx(){
     共享(同步)方法
    }
  3. 线程同步出现的问题

    死锁:
    1.在同步数据块内调用了其他同步代码-----减少同步锁定
    2.指定获取锁的顺序:比如规定,只有获得A锁的线程才有资格获取B锁
  4. synchronized

    1.synchronized进行加锁操作,synchronized的锁机制会根据线程竞争情况在运行时会有偏向锁(单一线程)、轻量锁(多个线程访问synchronized区域)、对象锁(重量锁,多个线程存在竞争的情况)、自旋锁等。该关键字是一个几种锁的封装。
    2.进入时,执行monitorenter(显式同步)将计数器+1,释放锁时计数器-1
    3.当一个线程判断到计数器为0时,则当前锁空闲,可以占用;反之,当前线程进入等待状态
  5. volatile

    1.volatile关键字是与Java的内存模型有关(主内存和工作内存)
    2.volatile可以直接与主内存产生交互,进行读写操作,保证可见性
    2.volatile能确保可见性:可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
    3.volatile禁止进行指令重排序。
    4.volatile变量可以保证下一个读取操作会在前一个写操作之后发生
  6. 线程池

    java.util.concurrent.ThreadPoolExecutor类就是一个线程池。
当前线程池大小:表示线程池中实际工作者线程的数量
最大线程池大小(maxinumPoolSize):表示线程池中允许存在的工作者线程的数量上限
核心线程大小(corePoolSize ):表示一个不大于最大线程池大小的工作者线程数量上限

线程code

  • 按顺序打印0~9--使用wait让未达到条件的线程进入等待态

    public class ThreadTest {
      private static final int endNum = 10;
      public static void main(String[] args) {
    //按需求新建多个线程,让每个线程实现对应的功能
          TTest[] test = new TTest[10];
          for(int i=endNum-1;i>=0;i--) {
              test[i] = new TTest(i);
              test[i].start();
          }
      }
    }
    public class TTest extends Thread {
      private static final int endNum = 10;
      static int orderNum = 0;
    //对象锁,只有获取该锁才能继续操作
      static Object object = new Object();
      private int printNum;
      public TTest(int printNum) {
          this.printNum = printNum;
      }
      @Override
      public void run() {
          synchronized (object) {
    //用户判断程序是否执行完毕
              while(orderNum < endNum) {
                  if(orderNum == printNum) {
                      System.out.println(printNum);
                      orderNum ++;
                      if(orderNum ==10) {
                          System.out.println("打印完成");
                      }
                      object.notifyAll();
                  }else {
                      try {
    //每个线程都对应一个参数,根据参数判断是否应该输出,如果不符合,当前线程就进入等待态,唤醒后再执行
                          object.wait();
                      } catch (Exception e) {
                          System.out.println("线程:"+printNum+"被打断了");
                          e.printStackTrace();
                      }
                  } 
              }
          }
      }
    }

ref:
java基础学习总结——线程(一)
你真的了解volatile关键字吗?
Java并发编程:volatile关键字解析

Set

  • hashSet

    无序,不可重复
public class Fish01 {
    public static void main(String args[]) {
        HashSet books = new HashSet();
        //分别向books集合中添加两个A对象,两个B对象,两个C对象
        books.add(new A());
        books.add(new A());
        books.add(new B());
        books.add(new B());
        books.add(new C());
        books.add(new C());
        System.out.println(books);
    }
}
class A{
    @Override
    public boolean equals(Object obj) {
        return true;
    }
}
class B {
    @Override
    public int hashCode() {
        return 1;
    }
}
//同时重写hashCode,equals方法,在集合中定义
class C {
    @Override
    public int hashCode() {
        return 2;
    }
    @Override
    public boolean equals(Object obj) {
        return true;
    }
}
//result
/home/sha/software/jdk/jdk-8u171-linux-x64/jdk1.8.0_171/bin/java -........
[cn.shafish.B@1, cn.shafish.B@1, cn.shafish.C@2, cn.shafish.A@677327b6, cn.shafish.A@14ae5a5]
Process finished with exit code 0
C类中重写hashCode,equals。定义两个对象相同,hashCode定位对象的存储位置(C类中返回hashCode值相同),equals判断对象值是否相等(C类中返回true)
B类中两个对象的引用值是不相等的,因为对象在内存中的地址单元不同(对于引用类型来说,默认是比较两个对象引用的地址)
A类对象的hashCode不同
  • LinkedHashSet

    元素的顺序总是与添加顺序一致(通过删除后再添加测试)
public class FIsh02 {
    public static void main(String[] args) {
        LinkedHashSet books = new LinkedHashSet();
        books.add("Java");
        books.add("LittleHann");
        System.out.println(books);
        //删除 Java
        books.remove("Java");
        //重新添加 Java
        books.add("Java");
        System.out.println(books);
    }
}
//result
[Java, LittleHann]
[LittleHann, Java]
  • TreeSet

    有序不重复
public class Fish03 {
    public static void main(String[] args) {
        TreeSet nums = new TreeSet();
        //向TreeSet中添加四个Integer对象
        nums.add(5);
        nums.add(2);
        nums.add(10);
        nums.add(-9);
        //输出集合元素,看到集合元素已经处于排序状态
        System.out.println(nums);
        //输出集合里的第一个元素
        System.out.println(nums.first());
        //输出集合里的最后一个元素
        System.out.println(nums.last());
        //返回小于4的子集,不包含4
        System.out.println(nums.headSet(4));
        //返回大于5的子集,如果Set中包含5,子集中还包含5
        System.out.println(nums.tailSet(5));
        //返回大于等于-3,小于4的子集。
        System.out.println(nums.subSet(-3 , 4));
    }
}
//result
[-9, 2, 5, 10]
-9
10
[-9, 2]
[5, 10]
[2]

List

  • ArrayList

    可以在创建它们时就指定initialCapacity大小,这样可以减少重新分配的次数,提高性能
public class Fish04 {
    public static void main(String[] args) {
        List books = new ArrayList();
        //向books集合中添加三个元素
        books.add(new String("算法导论"));
        books.add(new String("java编程思想"));
        books.add(new String("第一行代码"));
        System.out.println(books);
        //将新字符串对象插入在第二个位置
        books.add(1 , new String("疯狂Ajax讲义"));
        for (int i = 0 ; i < books.size() ; i++ )
        {
            System.out.println(books.get(i));
        }
        //删除第三个元素
        books.remove(2);
        System.out.println(books);
        //判断指定元素在List集合中位置:输出1,表明位于第二位
        System.out.println(books.indexOf(new String("疯狂Ajax讲义")));  //①
        //将第二个元素替换成新的字符串对象
        books.set(1, new String("LittleHann"));
        System.out.println(books);
        //将books集合的第二个元素(包括)
        //到第三个元素(不包括)截取成子集合
        System.out.println(books.subList(1 , 2));
    }
}
//result
[算法导论, java编程思想, 第一行代码]
算法导论
疯狂Ajax讲义
java编程思想
第一行代码
[算法导论, 疯狂Ajax讲义, 第一行代码]
1
[算法导论, LittleHann, 第一行代码]
[LittleHann]
  • Stack

    Stack后进先出
public class Fish05 {
    public static void main(String[] args) {
        Stack v = new Stack();
        //依次将三个元素push入"栈"
        v.push("算法导论");
        v.push("java编程思想");
        v.push("第一行代码");
        System.out.println(v);
        //访问第一个元素,但并不将其pop出"栈"
        System.out.println(v.peek());
        System.out.println(v);
        //pop出第一个元素
        System.out.println(v.pop());
        System.out.println(v);
    }
}
//result
[算法导论, java编程思想, 第一行代码]
第一行代码
[算法导论, java编程思想, 第一行代码]
第一行代码
[算法导论, java编程思想]