java理论知识笔记一:常见容易混淆的知识点
java基础
1跳出多重循环:
带有标号的的break语句,
public static void main(String[] args){
int[][] array = new int[][]{{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
int target = 4;
Here:
try {
boolean falg=true;
for(int i=0;i<array.length&&flag ;i++){//通过循环条件控制
System.out.println("i" + i );
for (int j = 0;j<array[0].length ;j++){
if (array[i][j]==target){
System.out.println("包含该整数");
break Here;
flag=false;
throw new Exception();//使用try/catch强制跳出循环
}
}
}
} catch (Exception e) {
}
}
2伪泛型
Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,这也就是通常所说类型擦除 。
3hashcode和equals
为什么重写 equals
时必须重写 hashCode
方法?
如果两个对象相等,则 hashcode 一定也是相同的。两个对象相等,对两个对象分别调用 equals 方法都返回 true。但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。因此,equals 方法被覆盖过,则 hashCode
方法也必须被覆盖。(为了保证效率)
4基本数据类型
Java中的几种基本数据类型是什么?对应的包装类型是什么?各自占用多少字节呢?
Java中有8种基本数据类型,分别为:
- 6种数字类型 :byte、short、int、long、float、double
- 1种字符类型:char
- 1种布尔型:boolean。
这八种基本类型都有对应的包装类分别为:Byte、Short、Integer、Long、Float、Double、Character、Boolean
基本类型 | 位数 | 字节 | 默认值 |
---|---|---|---|
int | 32 | 4 | 0 |
short | 16 | 2 | 0 |
long | 64 | 8 | 0L |
byte | 8 | 1 | 0 |
char | 16 | 2 | ‘u0000’ |
float | 32 | 4 | 0f |
double | 64 | 8 | 0d |
boolean | 1 | false |
对于boolean,官方文档未明确定义,它依赖于 JVM 厂商的具体实现。逻辑上理解是占用 1位,但是实际中会考虑计算机高效存储因素。
注意:
- Java 里使用 long 类型的数据一定要在数值后面加上 L,否则将作为整型解析:
char a = 'h'
char :单引号,String a = "hello"
:双引号
BigDecimal类
《阿里巴巴Java开发手册》中提到:浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用 equals 来判断。 具体原理和浮点数的编码方式有关。
此时可以使用BigDecimal类实现。
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
//加减乘除
a.add(b);
a.subtract(b);
a.multiply(b);
a.divide(b);
//还有转为基本类型的方法
5重写和重载
重写:重写发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写。
重载:是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理。
6深拷贝和浅拷贝
- 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
- 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
7Java 序列化中如果有些字段不想进行序列化,怎么办?
对于不想进行序列化的变量,使用 transient 关键字修饰。
transient 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和方法。
public class Apple implements Serializable {
private static final long serialVersionUID = -1315053955059469208L;
/**
* 主键id
*/
private String id;
/**
* 颜色
*/
private String color;
//不序列化此字段
private transient Integer weight;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
@Override
public String toString() {
return "Apple{" +
"id='" + id + '\'' +
", color='" + color + '\'' +
", weight=" + weight +
'}';
}
// public static boolean isGreenApple(Apple apple) {
// return "green".equals(apple.getColor());
// }
//
// public static boolean isHeavyApple(Apple apple) {
// return apple.getWeight() > 150;
// }
}
8.反射机制
(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取t对象的各种信息。
(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
- 静态编译: 在编译时确定类型,绑定对象
- 动态编译: 运行时确定类型,绑定对象
反射机制的优缺点:
-
优点: 运行期类型的判断,动态加载类,提高代码灵活度。
-
缺点: 1,性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的 java 代码要慢很多。
2,安全问题,让我们可以动态操作改变类的属性同时也增加了类的安全隐患。
在运行期间,一个类,只有一个Class对象产生。
反射机制的应用场景:
- 我们在使用 JDBC 连接数据库时使用
Class.forName()
通过反射加载数据库的驱动程序; - Spring 框架的 IOC(动态加载管理 Bean)创建对象以及 AOP(动态代理)功能都和反射有联系;
- 动态配置实例的属性;
9native关键字
调用本地方法。比如本地的c方法。
10 异常
在 Java 中,所有的异常都有一个共同的祖先 java.lang 包中的 Throwable 类。Throwable: 有两个重要的子类:Exception(异常) 和 Error(错误) ,二者都是 Java 异常处理的重要子类,各自都包含大量子类。
Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java 虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。
Exception(异常):是程序本身可以处理的异常。Exception 类有一个重要的子类 RuntimeException。RuntimeException 异常由 Java 虚拟机抛出。NullPointerException(要访问的变量没有引用任何对象时,抛出该异常)、ArithmeticException(算术运算异常,一个整数除以 0 时,抛出该异常)和 ArrayIndexOutOfBoundsException (下标越界异常)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jcoqdrZO-1607265830312)(D:\file\Java异常类层次结构图.png)]
11java多线程
线程,进程,程序的概念。他们之间的关系?
**程序:**是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,也就是说程序是静态的代码。
**进程:**程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。
**线程:**线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。
线程与进程的不同:
1线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。
2 进程属于操作系统的范畴,主要是同一段时间内,可以同时执行一个以上的程序,而线程则是在同一程序内几乎同时执行一个以上的程序段。
3同类的多个线程共享同一块 内存空间 和一组 系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。
线程的六个基本状态:
(1)NEW 线程构建。但还没有调用start方法
(2)RUNABLE 操作系统的的就绪和运行两种状态。
(3)BLOCKED 阻塞状态。线程阻塞与锁。
(4)WATING 等待状态。需要等待其他线程的通知或中断。
(5)TIME WAITING 超时等待。不同于waitting在指定时间自行返回。
(6)TERMINATED 线程执行完毕
12 IO流
java中的IOL流分类
- 按照流的流向分,可以分为输入流和输出流;
- 按照操作单元划分,可以划分为字节流和字符流;
- 按照流的角色划分为节点流和处理流。
既然有了字节流,为什么还要有字符流?
问题本质想问:不管是文件读写还是网络发送接收,信息的最小存储单元都是字节,那为什么 I/O 流操作要分为字节流操作和字符流操作呢?
回答:字符流是由 Java 虚拟机将字节转换得到的,
问题就出在这个过程还算是非常耗时,
并且,如果我们不知道编码类型就很容易出现乱码问题。
所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。
如果音频文件、图片等媒体文件用字节流(InputStream,OutputStream)比较好,如果涉及到字符的话使用字符流(Reader, Writer)比较好。
BIO,NIO,AIO 有什么区别?
-
BIO (Blocking I/O): 同步阻塞 I/O 模式,
数据的读取写入必须阻塞在一个线程内等待其完成。
在活动连接数不是特别高(小于单机 1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。
-
NIO (Non-blocking/New I/O): NIO 是一种同步非阻塞的 I/O 模型,
在 Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer 等抽象。
NIO 中的 N 可以理解为 Non-blocking,不单纯是 New。它支持面向缓冲的,基于通道的 I/O 操作方法。 NIO 提供了与传统 BIO 模型中的
Socket
和ServerSocket
相对应的SocketChannel
和ServerSocketChannel
两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞 I/O 来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发 -
AIO (Asynchronous I/O): AIO 也就是 NIO 2。
在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的 IO 模型。
异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步 IO 的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO 操作本身是同步的。查阅网上相关资料,我发现就目前来说 AIO 的应用还不是很广泛,Netty 之前也尝试使用过 AIO,不过又放弃了。