GenweiWu / Blog

个人技术能力提升
MIT License
4 stars 0 forks source link

2021_java基础 #70

Open GenweiWu opened 3 years ago

GenweiWu commented 3 years ago
GenweiWu commented 3 years ago

HashMap 和 Hashtable对比

速记:HashMap 非同步,非线程安全,性能好,允许null

HashMap HashTable
同步 非同步 同步(sychronized),线程安全
线程安全 非线程安全,另:java5提供了concurrentHashMap是线程安全的 线程安全
性能 性能好些 差一些
null 可以接受null的键值(key)和值(value) key和value都不能是null
public class HashTableDemo
{
    /**
     * HashMap的key和value都可以是null
     */
    @Test
    public void test()
    {
        HashMap<String, String> hashmap = new HashMap<>();
        hashmap.put(null, "xxx");
        hashmap.put("yyy", null);
        //{null=xxx, yyy=null}
        System.out.println(hashmap);
    }

    /**
     * Hashtable的key和value都不能是null
     */
    @Test
    public void test2()
    {
        Hashtable<String, String> hashtable = new Hashtable<>();
        //java.lang.NullPointerException
        hashtable.put(null, "xxx");
        //java.lang.NullPointerException
        hashtable.put("yyy", null);
    }
}

HashMap

jdk1.7 数组+链表(头部插入) jdk1.8 数组+链表(尾部插入)+红黑树

红黑树

平衡的二叉搜索树

  1. 所有节点红色或黑色
  2. 根节点是黑色
  3. 叶子节点都是空节点,黑色
  4. 红色节点的左右子节点都是黑色
  5. 根节点到任意子节点的路径上黑色节点数相同
GenweiWu commented 3 years ago

iterator vs listIterator

英[ɪtə'reɪtə]

iterator

listIterator

package com.njust.test.kemu2;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import org.junit.Test;

/**
 *
 */
public class IteratorDemo
{
    /***
     * Arrays.asList对应类型,不能是基本数据类型
     */
    @Test
    public void iterator00()
    {
        //此时list的大小为1
        int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
        List<int[]> list = Arrays.asList(array);
        System.out.println(list);

        Integer[] array222 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
        List<Integer> list222 = Arrays.asList(array222);
        //[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
        System.out.println(list222);
    }

    /**
     * iterator的主要方法:hasNext(),next(),remove()
     */
    @Test
    public void iterator01()
    {
        final String[] array = {"11", "22", "33", "44", "55"};
        List<String> stringList = new ArrayList<>(Arrays.asList(array));

        Iterator<String> iterator = stringList.iterator();
        while (iterator.hasNext())
        {
            String next = iterator.next();
            if (next.equals("44"))
            {
                iterator.remove();
            }
        }
        //[11, 22, 33, 55]
        System.out.println(stringList);
    }

    /**
     * listIterator除了hasNext(),next()和remove()外,还有:
     * - set方法用于修改元素
     */
    @Test
    public void listIterator01()
    {
        final String[] array = {"11", "22", "33", "44", "55"};
        List<String> stringList = new ArrayList<>(Arrays.asList(array));

        ListIterator<String> listIterator = stringList.listIterator();
        while (listIterator.hasNext())
        {
            String next = listIterator.next();
            if (next.equals("33"))
            {
                listIterator.set("33New");
            }
        }
        //[11, 22, 33New, 44, 55]
        System.out.println(stringList);
    }

    /**
     * 接着上面:
     * - set方法用于修改元素
     * - 可以反向遍历
     */
    @Test
    public void listIterator02()
    {
        final String[] array = {"11", "22", "33", "44", "55"};
        List<String> stringList = new ArrayList<>(Arrays.asList(array));

        //获取指定位置的迭代器
        ListIterator<String> listIterator = stringList.listIterator(stringList.size() - 1);
        while (listIterator.hasPrevious())
        {
            //迭代到前一个元素
            String previous = listIterator.previous();
            if (previous.equals("33"))
            {
                //可以修改元素
                listIterator.set("33_new");
            }
        }
        //[11, 22, 33_new, 44, 55]
        System.out.println(stringList);
    }

    /**
     * - 可以add添加元素
     */
    @Test
    public void listIterator03_a()
    {
        final String[] array = {"11", "22", "33", "44", "55"};
        List<String> stringList = new ArrayList<>(Arrays.asList(array));

        //获取指定位置的迭代器
        ListIterator<String> listIterator = stringList.listIterator(stringList.size() - 1);
        while (listIterator.hasPrevious())
        {
            //迭代到前一个元素
            String previous = listIterator.previous();
            if (previous.equals("33"))
            {
                //可以修改元素
                listIterator.add("33_prev");
            }
        }
        //[11, 22, 33_prev, 33, 44, 55]
        System.out.println(stringList);

    }

    /**
     * - 可以add添加元素
     */
    @Test
    public void listIterator03_b()
    {
        final String[] array = {"11", "22", "33", "44", "55"};
        List<String> stringList = new ArrayList<>(Arrays.asList(array));

        //获取指定位置的迭代器
        ListIterator<String> listIterator = stringList.listIterator();
        while (listIterator.hasNext())
        {
            //迭代到前一个元素
            String next = listIterator.next();
            if (next.equals("33"))
            {
                //可以修改元素
                listIterator.add("33_next");
            }
        }
        //[11, 22, 33, 33_next, 44, 55]
        System.out.println(stringList);

    }
}

   /**
     * set只能用Iterator不能用ListIterator
     */
    @Test
    public void test04()
    {
        Set<String> set = new HashSet<>();
        set.add("111");
        set.add("222");
        set.add("333");
        //set使用iterator迭代
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext())
        {
            String next = iterator.next();
            System.out.println("-->" + next);
        }

        List<String> list = new ArrayList<>(set);
        //list使用listIterator迭代
        ListIterator<String> listIterator = list.listIterator();
        while (listIterator.hasNext())
        {
            String previous = listIterator.next();
            System.out.println("next :" + previous);
        }

        //list使用listIterator倒序迭代
        ListIterator<String> listIterator222 = list.listIterator(list.size());
        while (listIterator222.hasPrevious())
        {
            String previous = listIterator222.previous();
            System.out.println("previous :" + previous);
        }
    }
GenweiWu commented 3 years ago

并行 vs 并发

并行是同时做,并发是一段时间内一起做(不是同时做,一般是交替进行)

总线程数<= CPU数量:并行运行

总线程数> CPU数量:并发运行

线程 vs 进程

https://blog.csdn.net/mxsgoden/article/details/8821936

  1. 一个程序至少有一个进程,一个进程至少有一个线程
  2. 进程有独立的内存单元,而进程下的多个线程共享内存

         -  一个进程挂掉不会影响其他的进程,而一个线程挂掉会导致对应的整个进程挂掉,所以多进程更稳定
         -  进程间切换代价大,线程的切换代价小
GenweiWu commented 3 years ago

守护线程

java中有两种线程,用户线程(User Thread)和守护线程(Daemon Thread)
  1. 守护线程是为其他线程服务的线程

  2. 所有非守护线程退出后,jvm就会退出

  3. 由于jvm退出的时候不会等待守护线程退出,所以守护线程不要去持有文件或资源,因为无法释放

package com.njust.test.thread;

public class DaemonThreadDemo
{

    public static void main(String[] args)
    {
        //test01();

        test02();

    }

    /**
     * 守护线程会直接退出,jvm不会等待守护线程执行完
     */
    private static void test02()
    {
        Thread userThread = new Thread(() -> {
            for (int i = 0; i < 5; i++)
            {
                try
                {
                    Thread.sleep(1000);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                System.out.println("UserThread:" + i);
            }
        });

        Thread daemonThread = new Thread(() -> {
            for (int i = 0; i < 50; i++)
            {
                try
                {
                    Thread.sleep(1000);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                System.out.println("DaemonThread:" + i);
            }
        });
        daemonThread.setDaemon(true);

        //启动测试
        daemonThread.start();
        userThread.start();

        // console:
        // DaemonThread:0
        // UserThread:0
        // DaemonThread:1
        // UserThread:1
        // UserThread:2
        // DaemonThread:2
        // DaemonThread:3
        // UserThread:3
        // UserThread:4
        // DaemonThread:4
    }

    /**
     * - 通过setDaemon(true)来设置守护线程
     * - 需要在start之前设置
     * - jvm不会等待守护线程执行完,就直接退出,所以没打印daemon run
     */
    private static void test01()
    {
        Thread thread = new Thread()
        {
            @Override
            public void run()
            {
                try
                {
                    Thread.sleep(1000);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                System.out.println("daemon run");
            }
        };
        thread.setDaemon(true);
        thread.start();

        System.out.println(thread.isDaemon());

        System.out.println("main run");
        // true
        // main run
    }
}
GenweiWu commented 3 years ago

创建线程的3种方法

/*
 * 创建线程的几种方法
 *
 * */
public class ThreadDemo
{
    /**
     * 方法1:继承thread类,复写run方法
     */
    @Test
    public void test01()
    {
        Thread thread = new Thread()
        {
            @Override
            public void run()
            {
                System.out.println("thread:" + Thread.currentThread().getName());
            }
        };
        thread.start();
        //thread:Thread-0
    }

    /**
     * 方法2:实现Runnable接口的run方法,然后new Thread(Runnable)创建线程
     */
    @Test
    public void test02()
    {
        Thread thread = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                System.out.println("Runnable:" + Thread.currentThread().getName());
            }
        });
        thread.start();
        //Runnable:Thread-0
    }

    @Test
    public void test03()
    {
        Callable<String> callable = new Callable<String>()
        {
            @Override
            public String call()
                throws InterruptedException
            {
                System.out.println("--->");
                Thread.sleep(1000);
                System.out.println("<---");
                return new Date().toString();
            }
        };

        FutureTask<String> futureTask = new FutureTask<>(callable);
        System.out.println("ready:");
        new Thread(futureTask).start();
        System.out.println("start:");

        try
        {
            String result = futureTask.get();
            System.out.println("result:" + result);
        }
        catch (InterruptedException | ExecutionException e)
        {
            e.printStackTrace();
        }
        System.out.println("finish");
        //        ready:
        //        start:
        //        --->
        //        <---
        //        result:Thu Mar 18 14:15:09 CST 2021
        //        finish
    }

}

同时extends thread和implements Runnable

package com.njust.test.learn;

/**
 * 实现线程有两种方法
 * 1、方法1是继承Thread类覆盖run方法
 * 2、方法2是new Thread(Runnable)的方式
 */
public class ThreadDemo
{
    public static void main(String[] args)
    {
        //3、这个线程的实际输出对应的是thread,原因可以去看下Thread的run方法的实现
        //
        //        @Override
        //        public void run() {
        //        if (target != null) {
        //            target.run();
        //        }
        //    }
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                System.out.println("runnable: " + Thread.currentThread().getName());
            }
        })
        {
            @Override
            public void run()
            {
                System.out.println("thread: " + Thread.currentThread().getName());
            }
        }.start();

    }
}
GenweiWu commented 3 years ago

线程的5种创建状态

  1. 创建状态:程序创建了,还没有执行start方法
  2. 就绪状态:执行了start方法,不过系统还没有将改线程设置为当前线程
  3. 运行状态:系统将该线程设置为当前线程,执行run方法
  4. 阻塞状态:等待资源或者调用sleep方法后,挂起状态
  5. 死亡状态:执行完run方法,不能再次使用start方法
GenweiWu commented 3 years ago

wait和notify

调用wait的线程会进入等待区,释放锁
调用notify会唤醒等待区的线程,notify会唤醒一个,notifyAll会唤醒所有
GenweiWu commented 3 years ago

线程安全三要素

GenweiWu commented 3 years ago

Synchronize 和 Lock的区别

1. synchronize是java关键字,而lock是类

2. lock需要手工控制获取锁,释放锁;而synchronize是自动获取锁和释放锁

lock.lock()
lock.unlock()

3. lock可以用tryLock来知道是否能获取到锁

tryLock()
如果可以获取到锁,则返回true
如果无法获取到锁,则直接返回false,而不会阻塞住

4. lock可以用tryLock(超时时间)来实现获取不到锁就放弃,而synchronize则会一直阻塞下去

// boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
lock.tryLock(3, TimeUnit.SECONDS);

5. lock获取锁的过程中,可以被中断

try
{
    lock.lockInterruptibly();
    //在等待获取锁的过程中可被中断
    try
    {
        //TODO
    }
    finally
    {
        lock.unlock();
    }
}
catch (InterruptedException e)
{
    e.printStackTrace();
}

Synchronize 和 ReentrantLock的区别

1. ReentrantLock是可重入锁,非公平锁(默认非公平锁,可自定义),Synchronize也是可重入锁,非公平锁(无法修改)

2. ReentrantLock可以有多个condition

例如为了实现三个线程按顺序打印12345的效果,就可以使用3个condition

3. 获取锁的信息

//例如看看当前是否是公平锁
public final boolean isFair() {
GenweiWu commented 3 years ago

反射

什么是反射?

java的反射是指在运行期可以获得一个对象所属信息(包括对象对应的类,对象的构造函数,对象的方法,对象的属性)

序列化

什么是 java 序列化?

将内存中对象的状态(属性,不包括方法)保存起来,而且可以反向将保存的对象还原到内存;

什么情况下需要序列化

example ```java import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import org.junit.Test; class ReflectData { public static final int SUCCESS = 0; public String name = "publicName"; protected String home = "china"; private String hobby = "jump"; public ReflectData() { } protected ReflectData(String name) { this.name = name; } private ReflectData(String name, String home) { this.name = name; this.home = home; } public void publicMethod() { System.out.println("publicMethod"); } protected void protectedMethod() { System.out.println("protectedMethod"); } } public class ReflectionTest { @Test public void testType() { Object reflectData = new ReflectData(); Class aClass = reflectData.getClass(); System.out.println(aClass); //class com.njust.test.kemu2.ReflectData } @Test public void testConstructor() { //1、获取public构造函数 Constructor[] constructors = ReflectData.class.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); } System.out.println("-------------------------->"); //2、获取所有构造函数 Constructor[] declaredConstructors = ReflectData.class.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } // public com.njust.test.kemu2.ReflectData() //--------------------------> //public com.njust.test.kemu2.ReflectData() //protected com.njust.test.kemu2.ReflectData(java.lang.String) //private com.njust.test.kemu2.ReflectData(java.lang.String,java.lang.String) } @Test public void testMethod() { //1、返回所有public方法, 包括父类的方法 Method[] methods = ReflectData.class.getMethods(); for (Method method : methods) { System.out.println(method); } System.out.println("-------------->"); //2、返回所有方法(包括public,protected,private), 不包括父类方法 Method[] declaredMethods = ReflectData.class.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println(method); } // public void com.njust.test.kemu2.ReflectData.publicMethod() //public final void java.lang.Object.wait() throws java.lang.InterruptedException //public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException //public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException //public boolean java.lang.Object.equals(java.lang.Object) //public java.lang.String java.lang.Object.toString() //public native int java.lang.Object.hashCode() //public final native java.lang.Class java.lang.Object.getClass() //public final native void java.lang.Object.notify() //public final native void java.lang.Object.notifyAll() //--------------> //public void com.njust.test.kemu2.ReflectData.publicMethod() //protected void com.njust.test.kemu2.ReflectData.protectedMethod() } @Test public void testField() throws InstantiationException, IllegalAccessException { ReflectData reflectionTest = ReflectData.class.newInstance(); //1、获取所有public属性 Field[] fields = ReflectData.class.getFields(); for (Field field : fields) { Object o = field.get(reflectionTest); System.out.println(field.getName() + "==>" + o); } System.out.println("------------------->"); //2、获取所有属性,但是private的要setAccessible(true) Field[] declaredFields = ReflectData.class.getDeclaredFields(); for (Field field : declaredFields) { field.setAccessible(true); Object o = field.get(reflectionTest); System.out.println(field.getName() + "==>" + o); } // SUCCESS==>0 // name==>publicName //-------------------> // SUCCESS==>0 // name==>publicName // home==>china // hobby==>jump } } ```
GenweiWu commented 3 years ago

代理模式

https://segmentfault.com/a/1190000011291179

1. 静态代理

代理对象和目标对象要实现一样的接口

缺点:

2. jdk动态代理

使用jdk api动态创建代理对象,又称为jdk代理接口代理

特点:

要求: 目标类要实现接口

3. cglib动态代理

cglib是一个第三方代码生成类库 动态生成一个子类来继承目标类,从而实现对目标类的功能扩展

要求:

动态代理

动态代理是什么?

动态创建一个代理类,给实现了某个接口的目标类,加上一些额外操作,比如日志和事务管理

有哪些应用?

GenweiWu commented 3 years ago

克隆

想对一个对象进行修改,又想保留之前对象的数据,就要用到克隆

如何实现克隆

实现克隆需要:
* 1. 实现cloneable接口
* 2. override方法clone
public class CloneDemo
{

    /**
     * 实现克隆需要:
     * 1. 实现cloneable接口
     * 2. override方法clone
     * @throws CloneNotSupportedException
     */
    @Test
    public void test()
        throws CloneNotSupportedException
    {
        Book book = new Book(1);
        Book book2 = (Book)book.clone();
        System.out.println(book.equals(book2));
    }

    static class Book implements Cloneable
    {
        private int price;

        public Book(int price)
        {
            this.price = price;
        }

        @Override
        protected Object clone()
            throws CloneNotSupportedException
        {
            return super.clone();
        }

        @Override
        public boolean equals(Object o)
        {
            if (this == o)
            {
                return true;
            }
            if (o == null || getClass() != o.getClass())
            {
                return false;
            }
            Book book = (Book)o;
            return price == book.price;
        }

        @Override
        public int hashCode()
        {
            return Objects.hash(price);
        }
    }
}

浅拷贝和深拷贝

利用 JSON.parse + JSON.stringify 可以实现深拷贝(不包括方法) 利用java序列化+反序列化 也可以实现深拷贝

GenweiWu commented 3 years ago

sessioncookie 有什么区别

session 的工作原理

服务器端保存的散列文件,类似了一个map,键就是sessionId

如果客户端禁止 cookie 能实现 session 还能用吗?

GenweiWu commented 3 years ago

spring mvc 和 struts 的区别

  1. 底层实现

    • struts2采用Filter实现
    • springmvc(DispatchServlet)采用Servlet实现
  2. 性能方面

    • struts2是类级别的拦截,每次请求对应一个新的Action;属性是共享的
    • springmvc是方法级别的拦截,性能更好;属性不共享
GenweiWu commented 3 years ago

sql注入

用户输入恶意的sql,来执行自己的sql语句,进而达到控制、破坏系统的目的

避免

xss攻击

跨站脚本攻击 用户向有漏洞的网站输入html代码,导致浏览的时候页面上会执行恶意html代码 比如姓名写成

避免

csrf

跨站请求伪造

用户访问A网站的时候,构造一个请求B网站的请求+用户刚好登陆了B网站,就可能导致B请求成功 比如用来盗号和转账等

避免

GenweiWu commented 3 years ago

try-catch-finally 中哪个部分可以省略?

  1. try catch
  2. try finally
  3. try catch finally
public class TryTest
{
    public static void main(String[] args)
    {
        //1. try catch
        try
        {
            System.out.println("try catch");
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        //2. try finally
        try
        {
            System.out.println("try finally");
        }
        finally
        {
            System.out.println("close resource...");
        }

        //3.try catch finally
        try
        {
            System.out.println("try catch finally");
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            System.out.println("finally");
        }

    }
}

Finally中赋值不生效,return生效

public class FinallyTest
{
    @Test
    public void test01()
    {
        int expect = 30;
        int actual = doTest01();
        Assert.assertEquals(expect, actual);
    }

    private int doTest01()
    {
        int num = 10;
        try
        {
            System.out.println(num / 0);
            //这里执行不到
            num = 20;
        }
        catch (Exception e)
        {
            num = 30;
            return num;
            //这里实际的语句是return 30,但是要去执行finally中的代码
        }
        finally
        {
            num = 40;
        }
        return num;
    }

    @Test
    public void test02()
    {
        int expect = 40;
        int actual = doTest02();
        Assert.assertEquals(expect, actual);
    }

    private int doTest02()
    {
        int num = 10;
        try
        {
            System.out.println(num / 0);
            //这里执行不到
            num = 20;
        }
        catch (Exception e)
        {
            num = 30;
            return num;
            //这里实际的语句是return 30,但是要去执行finally中的代码
        }
        finally
        {
            num = 40;
            return num;
            //这里重新生成返回路径,由于只能有一个return,所以这里直接返回40了
        }
    }

}
GenweiWu commented 3 years ago

状态码 301 和 302区别

301 永久重定向

forward 和 redirect 的区别

forward

redirect

GenweiWu commented 3 years ago

tcp 和 udp的区别

OSI七层模型 TCP/IP 概念5层模型 常见协议
应用层 1.应用层 HTTP,FTP,DNS,telnet
表示层 1.应用层 ---
会话层 1.应用层 ---
传输层 2.传输层 TCP ,UDP
网络层 3.网络层 IP,ICMP等
数据链路层 4.链路层 ARP,RARP等
物理层 4.链路层
UDP TCP
面向连接 无连接 面向连接
(建立连接3次握手,断开连接4次握手)
是否可靠 不可靠传输 可靠传输,有流量控制和拥塞控制
连接数 多对多 一对一
传输方式 面向报文,来一个发一个 面向字节流(可以攒够长度再发送)
首部开销 开销小,8字节 开销大,20~60字节
适用场景 视频直播 可靠的文件传输

TCP建立连接:3次握手

3次握手而不是两次握手的原因是:1和2让客户端A知道,他可以发送到服务端B,2和3是让B知道B可以发送到A,如果只有1和2,那B是不清楚自己发送给A的消息是否成功了的;

TCP断开连接:4次握手

基于3次握手的基础上继续理解,由于1和2是A告诉B自己不再发送了,所以达到目的:B不再继续接受数据

但是此时B还可以向A发送数据,而且还可能正在传输,所以等B传输完成,要发送3到A表示自己发送完了;A再发送4表示收到,于是B收到后就关闭了自己

GenweiWu commented 3 years ago

GET 和 POST请求的区别

GET请求 POST请求
1.多次请求 反复请求是无害的 导致二次提交,可能改变系统状态
2.浏览器书签 get请求的url可以被浏览器保存成书签 post不可以
3.缓存 get请求会被浏览器默认缓存cache post默认不缓存,除非手动修改
4.参数传递 get请求的参数通过url传递 post可以通过requestBody
4.参数传递 get请求的参数暴露在url上不安全
5.编码方式 get请求只能url编码 post请求有多种编码方式
6.长度 get请求参数长度有限制 post没有长度限制

post请求多种编码方式

GenweiWu commented 3 years ago

封装、继承、多态

GenweiWu commented 3 years ago

BIONIOAIO

1. BIO (Blocking IO) 同步阻塞IO

每个线程都在等待请求,多个线程在等待客户端连接过程中,都是处于阻塞状态;浪费了系统资源

socket.accept()、socket.read()、socket.write()三个主要函数都是同步阻塞的

2. NIO (Non-blocking IO) 同步非阻塞IO

IO多路复用

(事件分发器)1个线程进行事件轮询,有资源请求过来,可以进行读写的时候,再分发到各个channel进行处理

interface ChannelHandler{
    void channelReadable(java.nio.channels.Channel channel);
    void channelWritable(java.nio.channels.Channel channel);
}
class Channel{
    Socket socket;
    Event event;//读,写或者连接
}
//IO线程主循环:
class IoThread extends Thread{
    public void run(){
        Channel channel;
        while(channel= Selector.select()){//选择就绪的事件和对应的连接
            if(channel.event==accept){
                registerNewChannelHandler(channel);//如果是新连接,则注册一个新的读写处理器
            }
            if(channel.event==write){
                getChannelHandler(channel).channelWritable(channel);//如果可以写,则执行写事件
            }
            if(channel.event==read){
                getChannelHandler(channel).channelReadable(channel);//如果可以读,则执行读事件
            }
        }
    }
    Map<Channel,ChannelHandler> handlerMap;//所有channel的对应事件处理器
}
netty框架使用NIO

3. AIO 异步非阻塞IO

基于事件和回调机制

有请求需要处理时,才会触发回调函数进行处理

img

GenweiWu commented 3 years ago

StringBuilder、StringBuffer

 //1.反转使用StringBuilder、StringBuffer的reverse方法
 System.out.println(new StringBuilder("123").reverse());
 System.out.println(new StringBuffer("abc").reverse());

//2.一个是length()方法,一个是length属性
System.out.println("123".length());
int[] arr = {1, 2, 3};
System.out.println(arr.length);
GenweiWu commented 3 years ago

常量池

1. 字符串常量池

String str1 = "hello"; //str1指向静态区
String str2 = new String("hello");  //str2指向堆上的对象
String str3 = "hello";
String str4 = new String("hello");
System.out.println(str1.equals(str2)); //true
System.out.println(str2.equals(str4)); //true
System.out.println(str1 == str3); //true
System.out.println(str1 == str2); //false
System.out.println(str2 == str4); //false
System.out.println(str2 == "hello"); //false
str2 = str1;
System.out.println(str2 == "hello"); //true

2. 基本数据类型常量池

大部分基本数据类型,包括 byte, short, int, long, char, boolean(不包括float, double) 比如常见的int常量池,范围是-128~127

public static void main(String[] args) {
    Integer a = new Integer(3);
    Integer b = 3;  // 将3自动装箱成Integer类型
    int c = 3;
    System.out.println(a == b); // false 两个引用没有引用同一对象
    System.out.println(a == c); // true a自动拆箱成int类型再和c比较
    System.out.println(b == c); // true

    Integer a1 = 128;
    Integer b1 = 128;
    System.out.println(a1 == b1); // false

    Integer a2 = 127;
    Integer b2 = 127;
    System.out.println(a2 == b2); // true
}
GenweiWu commented 3 years ago

Collection接口

java容器分为 Collection和Map两大类

Collection分为 Set,List,Queue接口

img

集合安全类

1. vector,类似于arrayList的线程安全版本,不过性能较差,

可以用`Collections.synchronizedList`代替
1. vector每次扩容为*2,而arrayList扩容是*1.5,比较浪费空间
2. vector的同步是用synchronized实现的,性能较差

2. stack ,栈:先进后出

synchronized实现的线程安全类
`class Stack<E> extends Vector<E>`

代替

Deque<Integer> deque = new LinkedList<>();
deque.push(1);
deque.poll();
deque.pop();

3. hashtable,对应hashmap的线程安全版本(也是synchronized实现的)

推荐用ConcurrentHashMap来代替

List接口

1、 插入数据时,ArrayListLinkedListVector谁速度较快

ArrayListVector底层都是数组结构,可以随机访问,插入和删除要复制数组所以速度慢

LinkedList底层是链表,无法随机访问,插入和删除快

但是Vector是线程同步的,比ArrayList

所以 LinkedList> ArrayList > Vector

2、多线程情况下如何使用ArrayList

Collections.synchronizedList(list)


ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");

List synchronizedList = Collections.synchronizedList(list); for (String s : synchronizedList) { System.out.println(s); }


> 使用线程安全的 CopyOnWriteArrayList 代替线程不安全的 ArrayList。
```java
List<Object> list1 = new CopyOnWriteArrayList<Object>();

3、为什么 ArrayList 的 elementData 加上 transient 修饰?

重写了writeObject写法,自定义了序列化方法;

主要区别是:arraylist的内部数组的大小是有初始大小,这里只将有数据的大小进行序列化;

private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;
        s.defaultWriteObject();

        // Write out size as capacity for behavioural compatibility with clone()
        s.writeInt(size);

        // Write out all elements in the proper order.
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }

        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

Set接口

1、HashSet都实现原理

底层是HashMap,value是固定的PRESENT

2、如何检查重复

底层调用HashMap#put方法,先检查hash是否一致,再检查equals方法是否相等

1)equals相等,则覆盖

2)不相等,就是hash冲突要用链表/红黑树了

重复的时候就相当于put覆盖key了

Queue接口

poll和remove的区别

没有元素时,poll会返回null,而remove会抛异常NoSuchElementException

抛出异常 特殊值 阻塞 超时
插入 add(e) offer(e) put(e) offer(e, time, unit)
移除 remove() **poll()** take() poll(time, unit)
检查 element() peek() 不可用 不可用

Map接口

1、HashMap在1.7和1.8中的区别

1.7中 数组+链表

1.8中 数组+链表/红黑树(链表长度大于8时转换为树)

头插法在多线程时可能导致环形链表 https://www.cnblogs.com/youzhibing/p/13915116.html


## 在扩容transfer的时候,本来1->2,遍历的时候先处理1再处理2,头插法会导致链表变成2->1即链表的顺序被改变了

HashMap在jdk1.7中采用头插入法,在扩容时会改变链表中元素原本的顺序,以至于在并发场景下导致链表成环的问题。 而在jdk1.8中采用尾插入法,在扩容时会保持链表元素原本的顺序,就不会出现链表成环的问题了。



### 2、为啥HashMap用String或Integer作为key

1. final类型,不可变类型,保证key的hash不变
2. 同时复写了equals和hashcode方法

### 3、ConcurrentHashMap 的同步实现

1.7中  分段锁
1.8中  不再使用segment,使用CAS+synchronized锁头结点

### 4、Hashtable和ConcurrentHashMap的区别

1. HashTable底层是数组+链表,而HashMap的底层是jdk1.7时是 数据+链表,而1.8时是 数据+链表/红黑树

2. 实现同步的方式
Hashtable是方法都加上synchronized,put和get等方法,效率低
ConcurrentHashMap是 jdk1.7分段锁,1.8是CAS+synchronized头结点
GenweiWu commented 3 years ago

array和arrayList区别

  1. array中可以放基本数据类型和对象,arrayList只能放对象
  2. array固定大小,arrayList动态扩展的
  3. arrayList提供了很多有用的方法

Comparable 和 Comparator

package com.njust.test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class Student implements Comparable<Student>
{
    private String name;

    private int age;

    public int getAge()
    {
        return age;
    }

    public Student(String name, int age)
    {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Student o)
    {
        return name.compareTo(o.name);
    }
}

public class CompareDemo
{
    public static void main(String[] args)
    {
        Student student1 = new Student("zhangsan", 20);
        Student student2 = new Student("lisi", 30);
        List<Student> list = new ArrayList<>();

        /**
         * 1、实现了Comparable接口的CompareTo方法,是对象本身的排序
         */
        Collections.sort(list);

        /**
         * 2.或者使用外带的比较器:Comparator
         */
        Collections.sort(list, (s1, s2) -> {
            return s1.getAge() - s2.getAge();
        });
    }
}

TreeMap 和 TreeSet 在排序时如何比较元素?Collections 工具类中的 sort()方法如何比较元素?

  1. 参数对象实现Comparable接口,调用CompareTo方法

  2. 创建TreeSet或TreeMap时,动态传递Comparator比较器

  3. sort类似的两种方式

package com.njust.test;

import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import org.junit.Test;

class Student implements Comparable<Student>
{
    private String name;

    private int age;

    public int getAge()
    {
        return age;
    }

    public Student(String name, int age)
    {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Student o)
    {
        return name.compareTo(o.name);
    }
}

public class CompareDemo
{

    @Test
    public void treeMap()
    {
        Student student1 = new Student("zhangsan", 20);
        Student student2 = new Student("lisi", 30);

        //1,传递Comparator比较器
        Map<Student, Integer> treeMap1 = new TreeMap<>((s1, s2) -> {
            return s1.getAge() - s2.getAge();
        });
        treeMap1.put(student1, 1);
        treeMap1.put(student2, 2);

        //2.或者对象实现了Comparable接口
        Map<Student, Integer> treeMap2 = new TreeMap<>();
        treeMap2.put(student1, 1);
        treeMap2.put(student2, 2);
    }

    @Test
    public void treeSet()
    {
        Student student1 = new Student("zhangsan", 20);
        Student student2 = new Student("lisi", 30);

        //1,传递Comparator比较器
        Set<Student> treeSet = new TreeSet<>((s1, s2) -> {
            return s1.getAge() - s2.getAge();
        });
        treeSet.add(student1);
        treeSet.add(student2);

        //2.或者对象实现了Comparable接口
        Set<Student> treeSet2 = new TreeSet<>();
        //TreeSet的对象要实现Comparable接口才可以
        //Exception in thread "main" java.lang.ClassCastException: com.njust.test.Student cannot be cast to java.lang.Comparable
        treeSet2.add(student1);
        treeSet2.add(student2);

    }
}
GenweiWu commented 3 years ago

悲观锁、乐观锁

悲观锁

常见的synchronized和Lock都是悲观锁

乐观锁

CAS(Compare and swap)
例如 AtomicInteger中大量的cas操作
// ------------------------- JDK 8 -------------------------
// AtomicInteger 自增方法
public final int incrementAndGet() {
  return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

// Unsafe.class
public final int getAndAddInt(Object var1, long var2, int var4) {
  int var5;
  do {
      var5 = this.getIntVolatile(var1, var2);
  } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
  return var5;
}

// ------------------------- OpenJDK 8 -------------------------
// Unsafe.java
public final int getAndAddInt(Object o, long offset, int delta) {
   int v;
   do {
       v = getIntVolatile(o, offset);
       //这里一直在循环,也可以说在自旋
   } while (!compareAndSwapInt(o, offset, v, v + delta));
   return v;
}

自旋锁 VS 适应性自旋锁

自旋锁:

即有时候占用锁的线程A,占用时间比较短,很快就会释放锁的话,那当前等待的线程B,为了避免线程切换的开销,可以考虑执行while循环,来等待线程A释放锁;自旋就是while循环的过程;

不过,为了避免一直自旋导致浪费CPU时间,会控制自旋次数,一般是10

适应性自旋锁

就是自旋锁根据当前线程之前的表现,如果之前自旋5次就成功,就尝试也自旋5次;如果之前自旋都失败了,那就可能直接不尝试自旋

无锁、偏向锁、轻量级锁、重量级锁

这四种是指锁的状态,是针对synchronized的

1. 无锁

典型的CAS就是无锁 多个线程去修改同一个资源,只有一个能修改成功,其他的会不断重试

2. 偏向锁:只在单线程访问时生效,会偏向第一个访问锁的线程

3. 轻量级锁:获取不到锁,就先自旋

4. 重量级锁:获取不到锁就进行阻塞状态

优点 缺点 适用场景
偏向锁 加锁和解锁不需要额外的消耗,和执行非同步方法比仅存在纳秒级的差距。 如果线程间存在锁竞争,会带来额外的锁撤销的消耗。 适用于只有一个线程访问同步块场景。
轻量级锁 竞争的线程不会阻塞,提高了程序的响应速度。 如果始终得不到锁竞争的线程使用自旋会消耗CPU。 追求响应时间。同步块执行速度非常快。
重量级锁 线程竞争不使用自旋,不会消耗CPU。 线程阻塞,响应时间缓慢。 追求吞吐量。同步块执行速度较长。

公平锁 vs 非公平锁

公平锁(排队)

按照申请锁的顺序来获得锁

非公平锁(插队不成,才去排队)

线程加锁时,直接尝试获取锁,获取失败才去排队;

可能会出现后去获取锁的线程,反而先获得锁

可重入锁 vs 非可重入锁

可重入锁(也叫递归锁)

外层方法已经获取锁了,调用内层方法时自动获取锁(前提是锁对象一致)

public class Widget {
    public synchronized void doSomething() {
        System.out.println("方法1执行...");
        doOthers();
    }

    public synchronized void doOthers() {
        System.out.println("方法2执行...");
    }
}

独享锁 vs 共享锁

读写锁中,读锁就是共享锁、写锁就是互斥锁

读读不冲突、读写冲突+写写冲突

GenweiWu commented 3 years ago

基础数据类型

byte 1
short 2
int 4
long 8

float 4
double 8

char 2
boolean

整型:

byte:1个字节 8位 -128~127 short :2个字节 16位 int :4个字节 32位 long:8个字节 64位

浮点型:

float:4个字节 32 位 double :8个字节 64位

char类型:

char:2个字节。

GenweiWu commented 3 years ago

公平锁 vs 非公平锁

非公平锁

非公平锁时,队列链表中的第一个线程B,以及新来的线程C,会一起竞争机会的 但是,已经在队列中的线程还是挨个获取机会的

下图中非公平锁时,9,10,11的不同顺序可能是 ``` 1. 如果释放锁时,没有来新兔子,则此时等同于公平锁 2. 如果释放锁时,来了兔子C,而且此时队列中的第一个兔子还没反应过来,则此时兔子C抢先得到锁,体现了非公平锁 3. 如果释放锁时,来了兔子C,且队列中的第一个兔子B反应过来了,则兔子B抢到锁 ``` ![](https://upload-images.jianshu.io/upload_images/11183270-ba0934f9109b276b.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1024/format/webp)

公平锁

公平锁就是在获取锁之前会先判断等待队列是否为空或者自己是否位于队列头部,该条件通过才能继续获取锁。

GenweiWu commented 3 years ago

java8

https://github.com/GenweiWu/Blog/tree/master/java/java8