java基础知识补充

1. JDK和JRE

JSP部署Web应用程序为什么需要JDK呢?

因为应用程序服务器会将JSP转换成为Java Servlet,并且需要使用JDK来编译servlet。

2.Oracle JDK和 OpenJDK 的区别

Open JDK Oracle JDK
开源性 完全开源 不完全开源(是OpenJDK的一个实现)
稳定性 更加稳定(开发企业/商业软件,我建议您选择 Oracle JDK)
响应性和 JVM 性能 更好的性能
许可协议 GPL v2许可 二进制代码许可协议
费用 完全免费 企业/商业需要收费

3.字符常量和字符串常量

  • 字符常量相当于一个整形值(ASCII值),可以参与表达式运算;
  • 字符串常量代表一个地址值(该字符串在内存中的地址)

4.String、StringBuilder、StringBuffer的区别与联系

String StringBuilder StringBuffer
可变性 不可变:String类使用final关键字修饰字符数组来保存字符串,private final char value[] 可变:StringBuffer和StringBuilder都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串char[]value。但是没有用final关键字修饰。
线程安全性 线程安全 没有对方法进行加同步锁 对方法加了同步锁或者对调用的方法加了同步锁
性能 每次对String类型进行改变,都会生成一个新的String对象,然后将指针指向新的String对象 比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险 StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用
总结 操作少量的数据 多线程操作字符串缓冲区下操作大量数据 单线程操作字符串缓冲区下操作大量数据

5.在 Java 中定义一个不做事且没有参数的构造方法的作用

Java 程序在执行子类的构造方法之前,如果没有用 super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super()来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。

6.接口和抽象类的区别

抽象类 接口
继承 一个类只能实现一个抽象类 一个类可以有多个接口,接口自身可以通过extends关键字拓展多个接口
设计层面 抽象是类的抽象,是一种模板设计 接口是行为的抽象,是一种行为规范
方法修饰符 1.JDK1.8以前,protected 1. JDK1.8以前,public;
2.JDK1.8,default 2.JDK1.8,可以是public,可以是default
3.JDK1.9,可以是private

7.hashcode()和equals()的恩怨情仇

首先,其实hashcode()和equals()没啥关系,都是在Object.class中定义的方法,如果类没有重写equals方法的话,则会调用Object类中的equals方法,即判断两个对象的内存地址是否相同,等同于“==”(如果是同一个对象则为true)。

hashcode()只有在使用HashMap或者HashSet或者HashTable的时候才有用,要知道,他们的底层调用的都是通过HashMap实现的。

HashMap的组成结构为:数组和链表;

hashMap的存储:一个对象存储到hashMap中的位置是由其key 的hashcode值决定的;查hashMap查找key: 找key的时候hashMap会先根据key值的hashcode经过取余算法定位其所在数组的位置,再根据key的equals方法匹配相同key值获取对应相应的对象;
java基础知识补充_第1张图片
java基础知识补充_第2张图片

8.字节流和字符流

  • InputStream/Reader:所有输入流的基类,前者是字节输入流,后者是字符输入流。
  • OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流。

按照操作对象划分:

10.整数包装类值的比较

Integer x = 3;
Integer y = 3;
System.out.println(x == y);// true

当使用自动装箱方式创建一个Integer对象时,当数值在**-128 ~127**时,会将创建的 Integer 对象缓存起来,当下次再出现该数值时,直接从缓存中取出对应的Integer对象。所以上述代码中,x和y引用的是相同的Integer对象。

Integer x = 128;
Integer y = 128;
System.out.println(x == y);// false

但是当数值超过-128~127的范围之外,则必须要使用equals方法进行比较。

Integer a = new Integer(3);
Integer b = new Integer(3);
System.out.println(a == b);//false
System.out.println(a.equals(b));//true

所有整型包装类对象值的比较必须使用equals方法。

如果你的IDE(IDEA/Eclipse)上安装了阿里巴巴的p3c插件,这个插件如果检测到你用 ==的话会报错提示,推荐安装一个这个插件,很不错。

11. BigDecimal

浮点数之间的等值判断:

  • 基本数据类型不能用==比较;

  • 包装数据类型不能用equals来判断。

们在使用BigDecimal时,为了防止精度丢失,推荐使用它的 BigDecimal(String) 构造方法来创建对象。

12.Arrays.asList()注意事项

  • 使用工具类Arrays.asList()把数组转换成集合时,不能使用其修饰集合相关的方法,它的add/remove/clear方法会抛出UnsupportedOperationException异常。

    asList的返回对象是一个Arrays的内部类,并没有实现集合的修改方法,后台的数据仍是数组,因此当修改数组的值的时候,asList对应的数据也会修改。

    String[] str = new String[] {"you","are"};
    List<String> list = Arrays.asList(str);
    str[0] = "ccc";
    System.out.println(list.get(0)); //ccc
    
  • 传递的数组必须是对象数组,而不是基本类型数组。

    int[] myArray = { 1, 2, 3 };
    List myList = Arrays.asList(myArray);
    System.out.println(myList.size());//1
    System.out.println(myList.get(0));//数组地址值
    

    当传入一个原生数据类型数组时,Arrays.asList() 的真正得到的参数就不是数组中的元素,而是数组对象本身!此时List 的唯一元素就是这个数组,这也就解释了上面的代码。

    我们使用包装类型数组就可以解决这个问题。

    Integer[] myArray = { 1, 2, 3 };
    
  • ⭐️不要在foreach循环中进行元素的remove/add操作。remove元素要使用Iterator方式。

    原因:当使用foreach循环遍历ArrayList的时候,底层也是使用迭代器的hasNext()和next()方法来进行判断的。这个时候如果使用arraylist的remove方法进行操作,则会是的modCount数据加1,而exceptedCount不变,两者不相同,抛出java.util.ConcurrentModificationException异常,而使用iterator的remove方法时,会在最后重新赋值exceptedCount=modCount来保证两者数据的相同。

13.枚举类型使用

package com.bk.test;

/**
 * @author bear
 */

public enum PinType {

    /**
     * 注册使用
     */
    REGISTER(10000, "注册使用"),
    /**
     * 忘记密码使用
     */
    FORGET_PASSWORD(10001, "忘记密码使用"),
    /**
     * 更新手机号码
     */
    UPDATE_PHONE_NUMBER(10002, "更新手机号码使用");

    private final int code;
    private final String message;


    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    PinType(int code, String message) {
        this.code = code;
        this.message = message;
    }

    @Override
    public String toString() {
        return "PinType{" +
                "code=" + code +
                ", message='" + message + '\'' +
                '}';
    }
}

调用:

System.out.println(PinType.FORGET_PASSWORD.getCode());
System.out.println(PinType.FORGET_PASSWORD.getMessage());
System.out.println(PinType.FORGET_PASSWORD.toString());

显示:

10001
忘记密码使用
PinType{code=10001, message='忘记密码使用'}

14.this、super和static的区别

  • this和super是属于对象范畴的东西;
  • static静态方法是属于类范畴的东西。

15.List、Set、Map的区别

List Set Map
是否有序 有序 无序 无序
重复性 可以有多个元素引用相同的对象 不允许重复的集合,不会有多个元素引用相同的对象 两个key可以引用相同的对象,但key不能重复。
  • HashMap中,null可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为null。
  • HashMap不是线程安全的,如果需线程安全的话,可以使用ConcurrentHashMap。
HashMap HashSet
实现了Map接口 实现Set接口
存储键值对 仅存储对象
调用 put()向map中添加元素 调用 add()方法向Set中添加元素
HashMap使用键(Key)计算Hashcode HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,

16.hashCode()和equals()规定:

  1. 如果两个对象相等,则hashcode一定也是相同的
  2. 两个对象相等,对两个equals方法返回true
  3. 两个对象有相同的hashcode值,它们也不一定是相等的
  4. 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖
  5. hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。

17.HashMap的底层原理

  • 从存储结构上来说,HashMap是数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示:

java基础知识补充_第3张图片

那么,什么是红黑树呢?

  • 定义

    • 红黑树,一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。
    • 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
  • 性质

    • 每个结点要么是红的要么是黑的。
    • 根结点是黑的。
    • 每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。
    • 如果一个结点是红的,那么它的两个儿子都是黑的。
    • 对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。【黑色完美平衡】
      java基础知识补充_第4张图片

此图忽略了叶子和根部的父结点。同时,上文中我们所说的 “叶结点” 或"NULL结点",如上图所示,它不包含数据而只充当树在此结束的指示,这些节点在绘图中经常被省略,望看到此文后的读者朋友注意。

自平衡的最小单元:CPGU

java基础知识补充_第5张图片

  • 红黑树的新增:

java基础知识补充_第6张图片

  • put方法:

18.集合的选用原则

java基础知识补充_第7张图片

19.ArrayList

ArrayList继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable 这些接口。

  • RandomAccess接口:一个标志性接口,表明实现这个这个接口的 List 集合是支持快速随机访问的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。
  • Cloneable 接口:覆盖了clone(),能被克隆
  • java.io.Serializable接口:支持序列化能通过序列化去传输

和 Vector 不同,ArrayList 中的操作不是线程安全的!所以,建议在单线程中才使用 ArrayList,而在多线程中可以选择 Vector 或者 CopyOnWriteArrayList。

20. Java运行时数据区域

java基础知识补充_第8张图片

21. Java创建对象的过程

在这里插入图片描述

①类加载检查: 虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。

②分配内存:类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。分配方式“指针碰撞”“空闲列表” 两种,选择那种分配方式由 Java 堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定

内存分配的两种方式:(补充内容,需要掌握)

选择以上两种方式中的哪一种,取决于 Java 堆内存是否规整。而 Java 堆内存是否规整,取决于 GC 收集器的算法是"标记-清除",还是"标记-整理"(也称作"标记-压缩"),值得注意的是,复制算法内存也是规整的

java基础知识补充_第9张图片

内存分配并发问题(补充内容,需要掌握)

在创建对象的时候有一个很重要的问题,就是线程安全,因为在实际开发过程中,创建对象是很频繁的事情,作为虚拟机来说,必须要保证线程是安全的,通常来讲,虚拟机采用两种方式来保证线程安全:

  • CAS+失败重试: CAS 是乐观锁的一种实现方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性。
  • TLAB: 为每一个线程预先在Eden区分配一块儿内存,JVM在给线程中的对象分配内存时,首先在TLAB分配,当对象大于TLAB中的剩余内存或TLAB的内存已用尽时,再采用上述的CAS进行内存分配

③初始化零值: 内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

④设置对象头: 初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是那个类的实例、如何才能找到类的元数据信息、对象的哈希吗、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。

⑤执行 init 方法: 在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始,方法还没有执行,所有的字段都还为零。所以一般来说,执行 new 指令之后会接着执行 方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。

22. sleep()和wait()方法区别

  • sleep()方法没有释放锁,而wait()方法释放了锁。
  • Wait 通常被用于线程间交互/通信,sleep 通常被用于暂停执行。
  • wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法,或者可以使用 wait(long timeout)超时后线程会自动苏醒。sleep() 方法执行完成后,线程会自动苏醒。

23. start()方法和run()方法的区别

调用start方法可启动线程并使线程进入就绪状态,而run()方法只是thread的一个普通方法调用,还是在主线程里执行。

public class ThreadTest {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyRunable());
        Thread thread2 = new Thread(new MyRunable());
      	// start方法会异步的并发执行
        thread1.start();
        thread2.start();
      
      	// run方法则会顺序执行,还是在main主线程中
        //thread1.run();
        //thread2.run();
    }

    private static class MyRunable implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.print(i+", ");
            }
          	System.out.println();
        }
    }
}

start()的执行结果为:

0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 5, 6, 8, 7, 9, 8, 9, 

run()的执行结果为:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 

24. HTTP状态码

分类 分类描述
1** 信息,服务器收到请求,需要请求者继续执行操作
2** 成功,操作被成功接收并处理
3** 重定向,需要进一步的操作以完成请求
4** 客户端错误,请求包含语法错误或无法完成请求
5** 服务器错误,服务器在处理请求的过程中发生了错误

java基础知识补充_第10张图片

Servlet知识点

1. Servlet包:

  • javax.servlet
  • javax.servlet.http

2. HttpServlet

  • HttpServlet自定义了service()方法,并通过getMethod()方法判断请求类型,从而调用doGet()方法或doPost()方法。

3. Servlet生命周期

  • 调用init()方法进行初始化,第一次创建servlet时被调用。
  • 调用service()方法处理客户端请求,没收到一个servlet请求,产生一个新的线程并调用服务。
  • 调用destory()方法终止,调用后,servlet对象被标记为垃圾回收。
  • 由JVM的垃圾回收器进行垃圾回收。

4. response:

// 设置响应内容类型
response.setContentType("text/html");
request.setChartEncoding("utf-8");
request.getRequestDispathcer("url").forward(request,response);

你可能感兴趣的:(java基础知识补充)