JSP部署Web应用程序为什么需要JDK呢?
因为应用程序服务器会将JSP转换成为Java Servlet,并且需要使用JDK来编译servlet。
Open JDK | Oracle JDK | |
---|---|---|
开源性 | 完全开源 | 不完全开源(是OpenJDK的一个实现) |
稳定性 | 更加稳定(开发企业/商业软件,我建议您选择 Oracle JDK) | |
响应性和 JVM 性能 | 更好的性能 | |
许可协议 | GPL v2许可 | 二进制代码许可协议 |
费用 | 完全免费 | 企业/商业需要收费 |
String | StringBuilder | StringBuffer | |
---|---|---|---|
可变性 | 不可变:String类使用final关键字修饰字符数组来保存字符串,private final char value[] | 可变:StringBuffer和StringBuilder都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串char[]value。但是没有用final关键字修饰。 | |
线程安全性 | 线程安全 | 没有对方法进行加同步锁 | 对方法加了同步锁或者对调用的方法加了同步锁 |
性能 | 每次对String类型进行改变,都会生成一个新的String对象,然后将指针指向新的String对象 | 比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险 | StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用 |
总结 | 操作少量的数据 | 多线程操作字符串缓冲区下操作大量数据 | 单线程操作字符串缓冲区下操作大量数据 |
Java 程序在执行子类的构造方法之前,如果没有用 super()
来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super()
来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。
抽象类 | 接口 | |
---|---|---|
继承 | 一个类只能实现一个抽象类 | 一个类可以有多个接口,接口自身可以通过extends关键字拓展多个接口 |
设计层面 | 抽象是类的抽象,是一种模板设计 | 接口是行为的抽象,是一种行为规范 |
方法修饰符 | 1.JDK1.8以前,protected | 1. JDK1.8以前,public; |
2.JDK1.8,default | 2.JDK1.8,可以是public,可以是default | |
3.JDK1.9,可以是private |
首先,其实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值获取对应相应的对象;
按照操作对象划分:
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插件,这个插件如果检测到你用 ==的话会报错提示,推荐安装一个这个插件,很不错。
浮点数之间的等值判断:
基本数据类型不能用==比较;
包装数据类型不能用equals来判断。
们在使用BigDecimal时,为了防止精度丢失,推荐使用它的 BigDecimal(String) 构造方法来创建对象。
使用工具类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来保证两者数据的相同。
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='忘记密码使用'}
List | Set | Map | |
---|---|---|---|
是否有序 | 有序 | 无序 | 无序 |
重复性 | 可以有多个元素引用相同的对象 | 不允许重复的集合,不会有多个元素引用相同的对象 | 两个key可以引用相同的对象,但key不能重复。 |
HashMap | HashSet |
---|---|
实现了Map接口 | 实现Set接口 |
存储键值对 | 仅存储对象 |
调用 put() 向map中添加元素 |
调用 add() 方法向Set中添加元素 |
HashMap使用键(Key)计算Hashcode | HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性, |
那么,什么是红黑树呢?
定义:
性质:
此图忽略了叶子和根部的父结点。同时,上文中我们所说的 “叶结点” 或"NULL结点",如上图所示,它不包含数据而只充当树在此结束的指示,这些节点在绘图中经常被省略,望看到此文后的读者朋友注意。
自平衡的最小单元:CPGU
put方法:
ArrayList继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable 这些接口。
和 Vector 不同,ArrayList 中的操作不是线程安全的!所以,建议在单线程中才使用 ArrayList,而在多线程中可以选择 Vector 或者 CopyOnWriteArrayList。
①类加载检查: 虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
②分配内存: 在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。分配方式有 “指针碰撞” 和 “空闲列表” 两种,选择那种分配方式由 Java 堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
内存分配的两种方式:(补充内容,需要掌握)
选择以上两种方式中的哪一种,取决于 Java 堆内存是否规整。而 Java 堆内存是否规整,取决于 GC 收集器的算法是"标记-清除",还是"标记-整理"(也称作"标记-压缩"),值得注意的是,复制算法内存也是规整的
内存分配并发问题(补充内容,需要掌握)
在创建对象的时候有一个很重要的问题,就是线程安全,因为在实际开发过程中,创建对象是很频繁的事情,作为虚拟机来说,必须要保证线程是安全的,通常来讲,虚拟机采用两种方式来保证线程安全:
③初始化零值: 内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
④设置对象头: 初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是那个类的实例、如何才能找到类的元数据信息、对象的哈希吗、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。
⑤执行 init 方法: 在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始,方法还没有执行,所有的字段都还为零。所以一般来说,执行 new 指令之后会接着执行
方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。
调用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,
分类 | 分类描述 |
---|---|
1** | 信息,服务器收到请求,需要请求者继续执行操作 |
2** | 成功,操作被成功接收并处理 |
3** | 重定向,需要进一步的操作以完成请求 |
4** | 客户端错误,请求包含语法错误或无法完成请求 |
5** | 服务器错误,服务器在处理请求的过程中发生了错误 |
// 设置响应内容类型
response.setContentType("text/html");
request.setChartEncoding("utf-8");
request.getRequestDispathcer("url").forward(request,response);