# 一.基础定义
# 1.原码
原码(Sign-Magnitude Representation)是一种最直观的二进制数表示法,用于表示有符号整数
。在原码表示中,一个数的符号位
和数值
部分分开,其中最高位
表示符号
(0 表示正数,1 表示负数),其余位表示数值的绝对值。例如,8 位原码表示中,+5 为 00000101
,-5 为 10000101
。
然而,原码在进行数值运算时会引起问题,例如加法和减法运算会变得复杂。为了解决这个问题,引入了反码(One's Complement Representation)和补码(Two's Complement Representation)表示法。
总结:
原码
:直接表示数值和符号,容易理解,但不适用于加法和减法运算。反码
:解决了加法问题,但引入了减法的复杂性。补码
:在加法和减法运算中都很方便,广泛应用于计算机中。
需要注意的是,上述描述是基于整数的情况。在浮点数表示中,也会有类似的表示方法,但更加复杂。
# 2.反码
反码表示法
:在反码表示中,正数
的二进制表示与原码相同
,而负数
的表示方式则是正数的各位取反
(0 变 1,1 变 0)。符号位保持不变。例如,+5 的反码是 00000101
,-5 的反码是 11111010
。
反码的优势在于,它实现了负数
的加法与正数相同的方式,不过在进行减法
时,需要将减数取反
再进行加法。
# 3.补码
补码表示法
:补码表示法是目前计算机中最常用的有符号整数表示法。在补码中,正数
的二进制表示与原码相同,而负数
的表示方式是其反码加1
。这种表示方法可以简化加法
和减法
的运算,同时没有正零和负零的区分。
例如,+5 的 8 位补码是 00000101
,-5 的 8 位补码是 11111011
。
补码的优势在于,通过使用加法电路即可同时完成加法
和减法
操作,无需单独的减法电路。这大大简化了计算机的设计。
# 4.内部类
内部类不能加 static 的情况,静态内部类不能访问外部类非静态的变量和方法.
在 Java 中,非静态内部类(成员内部类、局部内部类、匿名内部类)不能加上 static 修饰符,因为非静态内部类是依赖于外部类的实例存在的,而 static 关键字表示该成员与类本身相关,不依赖于实例
。因此,如果在非静态内部类中使用 static 修饰符,就会出现无法访问外部类实例的问题。
例如,以下代码就是非法的:
public class Outer {
private int x = 10;
static class Inner {
static void display() {
System.out.println("x = " + x); // 编译错误:无法访问外部类的非静态成员
}
}
}
2
3
4
5
6
7
8
在静态内部类中,可以使用 static 修饰符。因为静态内部类不依赖于外部类的实例,可以直接使用外部类的静态成员和方法。
以下是静态内部类的示例代码:
public class Outer {
private static int x = 10;
static class Inner {
static void display() {
System.out.println("x = " + x);
}
}
}
2
3
4
5
6
7
8
# 5.什么是惰性求值?
惰性求值的意思是在运行计算代码时,直到执行该代码时才进行求值。
and 和 or 这两个逻辑运算符就具有惰性特点:
- 0 and 5 结果为 0,因为 0 为假,而 and 两边的值必须都为真才为真,所以 5 不会执行。
- 1 or 0 结果为 1,因为 1 为真,而 or 两边只要一个为真则表达式为真,所以 0 不会被执行到。
# 6.基本数据类型
基本数据类型所占字节以及取值范围
# 7.uint 和 int
指代不同:
1、uint:对应于无符号整数。
2、int:一种数据类型,在编程语言(C、C++、C#、Java 等)中,是用于定义整数类型变量的标识符。
取值范围不同:
1、uint:取值范围是 2^31 - 1,即:0~4294967295。
2、int:int 占用 4 字节,32 比特,数据范围为-2147483648~2147483647[-231 ~231 -1 ]
特点不同:
1、uint:有无符号的根本原因就是因为数据出现了溢出现象导致的。无符号值可以避免误存负数, 且扩大了表示范围。
2、int:除了 int 类型之外,还有 short、long、long long 类型可以表示整数。
# 8.println 底层原理
public void println(boolean x) {
synchronized (this) {
print(x);
newLine();
}
}
2
3
4
5
6
# 9.java 实体
- PO :(persistant object ),持久对象
- VO :(value object) ,值对象
- DAO :(Data Access Objects) ,数据访问对象接口
- BO :(Business Object),业务对象层
- DTO (Data Transfer Object) 数据传输对象
- POJO :(Plain Old Java Objects),简单的 Java 对象
# 二.代码基础
# 1.数组转集合
public static void main(String[] args) {
String[] str = new String[]{"a", "b"};
List list = Arrays.asList(str);
list.add("c");//报错
// str[0] = "d";
for (Object o : list) {
System.out.println(o);
}
}
2
3
4
5
6
7
8
9
踩坑姿势: Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。 asList() 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。
解决方案: 在转换之前操作咯。还需要注意一点,在你转换后,再对数组的值进行修改时,集合也会跟着变哦(注释掉的代码)。
# 2.遍历删除
public static void main(String[] args) {
List<String> a = new ArrayList<>();
a.add("1");
a.add("2");
a.add("3");
for (String temp : a) {
if ("2".equals(temp)) {
a.remove(temp);
}
}
Iterator<String> it = a.iterator();
while (it.hasNext()) {
String temp = it.next();
if ("2".equals(temp)) {
it.remove();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式(代码第二种),如果并发操作,需要对 Iterator 对象加锁。
# 3.集合转换数组
public class ListToArray {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("你好");
list.add("傻瓜");
String[] objects = (String[]) list.toArray();
System.out.println(objects);
}
}
2
3
4
5
6
7
8
9
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
at com.example.demo.ListToArray.main(ListToArray.java:11)
会报 ClassCastException 异常。
踩坑姿势: 直接使用 toArray() 无参方法返回值只能是 Object[]类,若强转其它类型数组将会抛异常。
//解决方案: 使用 <T> T[] toArray(T[] a); 有参数这个方法,代码如下:
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("你好");
list.add("傻瓜");
String[] array = new String[list.size()];
array = list.toArray(array);
System.out.println(array);
}
2
3
4
5
6
7
8
9
# 4.CSV 中文乱码
CSV 文件导入数据库中文乱码
做好的 CSV 直接导入数据库,中文是这样的
������
解决办法
- 把. csv 后缀改成. txt
- 打开 txt,选择另存为,编码方式改成 UTF-8
- 把. txt 后缀改为. csv
- 导入,成功!
# 5.中断外层循环
在 Java 中,要中断外层循环,可以使用带标签的 break 语句。具体来说,您可以在外层循环之前使用标签来标识该循环,然后在内层循环中使用带有该标签的 break 语句来中断外层循环。
以下是示例代码:
outerloop:
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if (i * j > 6) {
System.out.println("Breaking");
break outerloop;
}
System.out.println(i + " " + j);
}
}
System.out.println("Done");
2
3
4
5
6
7
8
9
10
11
在此示例中,我们使用名为“outerloop”的标签来标识外层循环。在内层循环中,如果 i * j 的值大于 6,则使用带有标签的 break 语句中断外层循环。如果没有中断外层循环,则最后一行的“Done”消息将被打印。否则,该消息将不会被打印。
# 6.初始化列表
在 Java 中,可以使用以下语法来初始化并赋值一个字符串列表
List<String> myList = Arrays.asList("item1", "item2", "item3");
这将创建一个包含三个字符串元素的列表。您可以根据需要添加或删除元素。
# 三.常见问题
# 1.对象创建过程
下面通过通俗的语言分解对象创建中的核心过程(忽略不重要的部分)
- 在栈内存空间分配名为 user 的内存
- 在堆内存空间以 User 类为模版分配内存空间
- 成员变量初始化零值
- 成员变量初始化
- init 初始化
在执行完 init 方法之后,便可以通过 get 方法访问到具体的成员变量值。
# 2.接⼝与抽象类区别
参数 | 抽象类 | 接口 |
---|---|---|
默认实现方法 | 可以有 | 不存在(jdk.8 之前) |
实现 | extends | implements |
构造器 | 可以有 | 不能有 |
普通类区别 | 不能实例化 | 类型不同 |
访问权限 | public,protected,default | 默认 public 不可以其他 |
多继承 | 不可以 | 可以 |
⼀个类声明可否既是 abstract 的,⼜是 final 的? 不能,这两个修式符⽭盾(abstract 就是要被继承)
抽象类不⼀定包含抽象⽅法
有抽象⽅法,则⼀定是抽象类
抽象类不能被实例化,⼀般⽤作基类使⽤;
类可以实现多个接⼝但只能继承⼀个抽象类
接⼝⾥⾯所有的⽅法都是 Public 的,抽象类允许 Private、Protected ⽅法
JDK8 前接⼝⾥⾯所有的⽅法都是抽象的且不允许有静态⽅法,抽象类可以有普通、静态⽅法,JDK8 接⼝可以实现默认⽅法 和静态⽅法,前⾯加 default、static 关键字
# 3.值传递与引用传递
对于基本类型 num ,赋值运算符会直接改变变量的值,原来的值被覆盖掉。 对于引用类型 str,赋值运算符会改变引用中所保存的地址,原来的地址被覆盖掉。但是原来的对象不会被改变(重要)。如上图所示,"hello" 字符串对象没有被改变。(没有被任何引用所指向的对象是垃圾,会被垃圾回收器回收)
int num = 10;
String str = "hello";
num = 20;
str = "java";
2
3
4
5
# 4.object 类下的方法
Object 是 Java 中所有类的根类,因此它的方法可以被所有的 Java 类继承和使用。下面是 Object 类中一些常用的方法:
equals(Object obj)方法
:比较两个对象是否相等,返回一个 boolean 类型的值。hashCode()方法
:返回对象的哈希码,是一个 int 类型的值。toString()方法
:返回对象的字符串表示形式,通常用于调试和日志输出。getClass()方法
:返回对象的运行时类,是一个 Class 类型的值。notify()方法
:唤醒在此对象监视器上等待的一个线程。notifyAll()方法
:唤醒在此对象监视器上等待的所有线程。wait()方法
:使当前线程等待,直到另一个线程调用此对象的 notify()或 notifyAll()方法。finalize()方法
:当 Java 虚拟机确定不存在对该对象的更多引用时,会调用此方法。clone()方法
:clone() 方法是 Object 类中的一个方法,用于创建并返回一个对象的副本。它是 Java 中实现对象克隆的一种方式。
这些方法是 Java 中 Object 类中比较常用的方法,还有一些其他的方法,可以查看 Java 官方文档进行学习。
# 5.clone 方法?
clone()
方法是 Object
类中的一个方法,用于创建并返回一个对象的副本。它是 Java 中实现对象克隆的一种方式。
下面是 clone()
方法的声明:
protected native Object clone() throws CloneNotSupportedException;
需要注意的是,clone()
方法是一个受保护的方法,因此只能在对象的子类中访问。此外,clone()
方法声明了一个可能抛出 CloneNotSupportedException
异常的检查异常。
默认情况下,clone()
方法执行的是浅拷贝(shallow copy),即它会创建一个新的对象,将原始对象的字段值复制给新对象,并将引用类型的字段直接复制引用,因此新对象和原始对象将共享相同的引用对象。如果需要实现深拷贝(deep copy),则需要在 clone()
方法的实现中进行相应的处理。
要使一个类支持克隆,需要满足以下两个条件:
- 类必须实现
Cloneable
接口,该接口是一个标记接口,没有任何方法。 - 类必须覆盖
clone()
方法并将其访问修饰符改为public
。
# 5.java 创建实例的方式
1、关键字 new。工厂模式是对这种方式的包装;
2、类实现克隆接口,克隆一个实例。原型模式是一个应用实例;
3、用该类的加载器,newinstance。java 的反射,反射使用实例:Spring 的依赖注入、切面编程中动态代理
4、sun.misc.Unsafe 类,allocateInstance 方法创建一个实例。(Java 官方也不建议直接使用的 Unsafe 类,据说 Oracle 正在计划从 Java 9 中去掉 Unsafe 类)
5、实现序列化接口的类,通过 IO 流反序列化读取一个类,获得实例。
# 6.String 的优缺点
String 的优缺点:
优点:
使用 String 时,如果字符串发生修改,那么修改后的内容会保存在内存中的不同位置,这样的话,内存中就同时有原始的字符串值和修改后的字符串值。缺点:
对于每一个这样的操作,它会消耗更多的内存,因为它把修改后的字符串值存储在新的内存空间中。因此它会导致性能问题。解决方案:
要解决这个性能问题,开发者应该使用 StringBuilder 或者 StringBuffer 来实现字符串的修改,然后再转换成 String 对象把字符串传递给用户。- Object 对 equals()方法的定义是:对象 a 和 b 如果是同一个对象的引用,那么 a.equals(b)返回 true,否则返回 false。而 String 类重写了 Object 的 equals()方法,String 对象的 equals()方法比较的是内容,内容相等则返回 true。StringBuffer 和 StringBuilder 则没有重写 equals()方法,与 Object 的 equals()方法的定义相同。
StringBuffer 和 StringBuilder 的优缺点:
优点:
StringBuffer 和 StringBuilder 有更好的性能,因为它们对内存的消耗更少,所有对字符串的修改操作都存储在同一内存位置。缺点:
修改前的原始字符串值不会被保留。
# 7.String 类
String 和 StringBuffer 和 StringBuilder 的区别有哪些?
String 是不可变字符序列。
StringBuffer 是可变的字符序列。方法使用 synchronized 修饰,保证同步.
StringBuilder 也是可变的字符序列。并没有使用 synchronized 修饰.
要知道 StringBuilder 和 StringBuffer 的内部实现跟 String 类一样,都是通过一个 char 数组存储字符串的,不同的是 String 类里面的 char 数组是 final 修饰的,是不可变的,而 StringBuilder 和 StringBuffer 的 char 数组是可变的。
# 8.int 和 Integer 的区别
数据类型:
在 java 当中,一共有 8 种数据类型,其中有 4 种整形,2 种浮点型,1 中用于表示 Unicode 编码的字符类型 char,和一种用于表示真假的 boolean 类型
- 4 种整型:int、short、long、byte
- 2 种浮点型:float、double
- 字符类型:char
- 真假类型:boolean
包装类的作用:
在java
当中,每个基本数据类型都对应了一个包装类,包装类的存在解决了基本数据类型无法做到的事情泛型类型参数、序列化、类型转换、高频区间数据缓存等问题。
基础类型 | 包装类型 |
---|---|
int | Integer |
short | Short |
byte | Byte |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
int 和 Integer 的区别:
- 数据类型不同:int 是基本数据类型,Integer 是包装数据类型
- 默认值不同:int 默认值是 0,而 Integer 默认值是 null
- 内存中存储方式不同:int 在内存中直接存储的是数据值,而 Integer 实际存储的是对象的引用,当 new 一个 Integer 时实际是生成一个指针指向此对象
- 实例化方式不同:Integer 必须实例化才能使用,而 int 不需要
- 变量的比较方式不同:int 可以使用==来判断是否相等,而 Integer 一定要使用 equals 来比较两个变量是否相等。
# 9.switch 不支持 long?
switch 底层是使用 int 型 来进行判断的,即使是枚举、String 类型,最终也是转变成 int 型。由于 long 型表示范围大于 int 型,因此不支持 long 类型。
# 10.System.arraycopy()
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String email;
}
2
3
4
5
6
7
8
public class SystemArrayCopy {
public static void main(String[] args) {
//初始化对象数组
User[] users = new User[]{new User(1, "admin", "admin@qq.com")
, new User(2, "maco", "maco@qq,com")
, new User(3, "kitty", "kitty@qq,com")};
//新建一个目标对象数组
User[] target = new User[users.length];
//实现复制
System.arraycopy(users, 0, target, 0, users.length);
System.out.println("源对象与目标对象的物理地址是否一样:" + (users[0] == target[0] ? "浅复制" : "深复制"));
target[0].setEmail("admin@sina.com");
System.out.println("修改目标对象的属性值后源对象users:");
for (User user : users) {
System.out.println(user);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 11.++i 和 i++
具体来说,i++
表示先返回变量 i
的原始值,然后再将变量 i
加 1。而 ++i
表示先将变量 i
加 1,然后再返回变量 i
的新值。以下是示例代码:
int i = 0;
System.out.println(i++); // 输出 0,此时 i 的值为 1
System.out.println(++i); // 输出 2,此时 i 的值为 2
2
3
在上述示例代码中,System.out.println(i++)
首先输出变量 i
的原始值 0,然后再将变量 i
加 1,此时 i
的值变为 1。而 System.out.println(++i)
先将变量 i
加 1,此时 i
的值变为 2,然后再输出变量 i
的新值 2。
# 12.2 的 31 次方
2 的 31 次方等于 2,147,483,648。大约是 21 亿多,要有一个大致的概念。
# 13.LTS
长期支持(Long Term Support,LTS)是一种软件开发和维护策略,通常用于操作系统、软件框架、库和其他类型的软件项目。LTS 的目标是为用户提供长期的稳定性和可维护性,尤其是在大规模企业环境中,以减少更新和升级的频率,降低成本,同时确保软件的稳定性和安全性。以下是关于 LTS 的详细解释:
长期支持版本的定义:
- LTS 版本是软件项目的一个特定版本,通常会获得更长时间的维护和更新支持,以确保其在数年甚至更长时间内仍然可用和安全。这些版本通常被选定为长期支持版本,以满足企业和组织的长期需求。
支持时间周期:
- LTS 版本通常有一个明确的支持时间周期,例如 3 年、5 年或更长。在这个时间内,开发者会提供安全更新、错误修复和有限的功能增强。这使得组织可以在一段时间内持续使用软件,而不必频繁升级到新版本。
安全性和可维护性:
- 长期支持版本的关键目标之一是确保软件的安全性。这意味着开发者会及时发布针对已知漏洞的安全修复,以保护用户免受潜在威胁。
- 长期支持版本还注重软件的可维护性,确保在长期内能够继续修复和改进软件。这通常需要有一个专门的团队来负责维护和支持 LTS 版本。
预测性和稳定性:
- 组织和企业可以更好地规划他们的 IT 基础设施,因为他们知道 LTS 版本会有一个明确的生命周期。这使得他们可以减少意外升级和与软件版本相关的问题,以确保系统的稳定性。
适用范围:
- 长期支持版本不仅适用于操作系统,还适用于各种类型的软件,包括数据库管理系统、Web 框架、编程语言等。例如,Ubuntu Linux 的 LTS 版本、Java 的 LTS 版本以及各种开源软件项目的 LTS 版本都非常常见。
LTS 是一种为用户提供长期支持和可维护性的软件开发和维护策略,有助于组织和企业管理其 IT 基础设施,降低成本,并确保其系统的安全性和稳定性。它在企业环境中特别有价值,因为那里的系统和应用程序需要长期运行和支持。
# 14.同比和环比
- 同比发展速度主要是为了消除季节变化的影响,用以说明本期发展水平与去年同期发展水平对比而达到的相对发展速度;
- 环比发展速度是报告期水平与前一时期水平之比,表明先行时期的发展速度。
# 15.时间戳
时间戳10位和13位的区别
时间戳 10 位是指时间戳精确到秒,包含 10 位整数
时间戳 13 位是指时间戳精确到毫秒,包含 13 位整数
两者之间转换时,只需乘以 1000 或者除以 1000 即可转换
时间戳示例:
以下是一个 Python 代码示例,演示如何获取当前时间戳并将其转换为日期时间格式:
import time
from datetime import datetime
# 获取当前时间戳(单位:秒)
timestamp = time.time()
# 将时间戳转换为日期时间格式
datetime_obj = datetime.fromtimestamp(timestamp)
# 打印结果
print("当前时间戳(秒):", timestamp)
print("当前日期时间:", datetime_obj)
2
3
4
5
6
7
8
9
10
11
12
这段代码首先导入了 time
模块和 datetime
类,然后使用 time.time()
函数获取当前时间戳,接着使用 datetime.fromtimestamp()
函数将时间戳转换为日期时间对象。最后,打印出当前时间戳和对应的日期时间。
# 16. OkHttp 超时设置示例
在 OkHttp 中,超时设置是通过OkHttpClient
的newBuilder()
方法链式配置的。以下是一个简单的示例,展示了如何设置读取超时为 60 秒:
OkHttpClient client = new OkHttpClient().newBuilder()
.readTimeout(60, TimeUnit.SECONDS)
.build();
2
3
这段代码创建了一个OkHttpClient
实例,并将读取超时设置为 60 秒。这意味着,如果服务器在 60 秒内没有发送完响应数据,客户端将取消这次请求。
# 17. 连接超时和写入超时的设置
除了读取超时,连接超时和写入超时同样重要。以下是如何设置连接超时和写入超时的示例:
OkHttpClient client = new OkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS) // 设置连接超时为10秒
.writeTimeout(20, TimeUnit.SECONDS) // 设置写入超时为20秒
.readTimeout(60, TimeUnit.SECONDS) // 设置读取超时为60秒
.build();
2
3
4
5
在这个示例中,我们设置了连接超时为 10 秒,写入超时为 20 秒,以及读取超时为 60 秒。这样的设置可以确保客户端在网络请求的各个阶段都有合理的超时控制。
# 18.@Scheduled
毫秒
以下是 @Scheduled
注解的一些常用属性:
cron
: 这是最常用的属性,用于指定一个 cron 表达式来定义任务的执行计划。cron 表达式是一种字符串,用于表达时间和日期的重复模式。zone
: 时区属性,用于指定任务执行的时区。fixedRate
: 指定任务执行的间隔时间,单位为毫秒。如果指定了fixedRate
,则cron
表达式将被忽略。fixedDelay
: 指定任务执行的延迟时间,单位为毫秒。与fixedRate
不同的是,fixedDelay
是从上一次任务结束时开始计算延迟。initialDelay
: 指定任务首次执行前的延迟时间,单位为毫秒。z
: 用于指定任务的优先级。
使用 @Scheduled
注解的示例代码如下:
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class ScheduledTasks {
// 使用 cron 表达式定义任务计划
@Scheduled(cron = "0 * * * * ?")
public void executeTask() {
System.out.println("执行定时任务:" + LocalDateTime.now());
}
// 使用 fixedRate 属性定义任务计划
@Scheduled(fixedRate = 5000)
public void executeTaskWithFixedRate() {
System.out.println("执行固定速率定时任务:" + LocalDateTime.now());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在这个示例中,executeTask
方法将根据 cron 表达式 "0 * * * * ?" 每分钟执行一次。而 executeTaskWithFixedRate
方法将每 5 秒执行一次。
# 19.定义set
// 静态集合,存储数据
private static final Set<String> DATA_SET = new HashSet<>();
// 静态初始化块,用于初始化集合数据
static {
DATA_SET.add("您好,123。");
DATA_SET.add("您好,456。");
DATA_SET.add("您好,789。");
}
/**
* 随机选择集合中的一个元素
*
* @return
*/
public static String getRandomElement() {
Random random = new Random();
// 使用迭代器遍历集合并随机选择一个元素
return DATA_SET.stream().skip(random.nextInt(DATA_SET.size())).findFirst().orElse(null);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 20.单字枚举
@Getter
@ToString
public enum MailInfoMsgEnum {
/**
* 加好友消息
*/
friend,
/**
* 房间邀请
*/
room,
/**
* 消息
*/
msg
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
MailInfoMsgEnum.friend.name()