java基础

java 基础

java特性

  1. java具有跨平台性
  2. java语言面向对象
  3. java语言是健壮的,强类型机制,异常处理,垃圾的自动收集等是java健壮性的重要保证
  4. java是解释型的(由于java在JVM上解释运行)

java编译与流程

1
2
javac test.java
java test //运行的是test的类,而不是test.class的类
1
.java文件 -> javac编译 -> .class文件 -> java运行

java开发细节

  1. java程序的入口是main()方法,具有固定的书写格式
  2. 一个java文件中只能存在一个public类,但是类可以多个
  3. 编译之后,每一个类都对应一个.class文件
  4. 文件名和public类名相同
  5. main入口不一定在public类中

文档注释

1
javadoc -d 文件名 标签 test.java

javadoc 标签

javadoc 工具软件识别以下标签:

标签 描述 示例
@author 标识一个类的作者 @author description
@deprecated 指名一个过期的类或成员 @deprecated description
{@docRoot} 指明当前文档根目录的路径 Directory Path
@exception 标志一个类抛出的异常 @exception exception-name explanation
{@inheritDoc} 从直接父类继承的注释 Inherits a comment from the immediate surperclass.
{@link} 插入一个到另一个主题的链接 {@link name text}
{@linkplain} 插入一个到另一个主题的链接,但是该链接显示纯文本字体 Inserts an in-line link to another topic.
@param 说明一个方法的参数 @param parameter-name explanation
@return 说明返回值类型 @return explanation
@see 指定一个到另一个主题的链接 @see anchor
@serial 说明一个序列化属性 @serial description
@serialData 说明通过writeObject( ) 和 writeExternal( )方法写的数据 @serialData description
@serialField 说明一个ObjectStreamField组件 @serialField name type description
@since 标记当引入一个特定的变化时 @since release
@throws 和 @exception标签一样. The @throws tag has the same meaning as the @exception tag.
{@value} 显示常量的值,该常量必须是static属性。 Displays the value of a constant, which must be a static field.
@version 指定类的版本 @version info

Java 关键字

下面列出了 Java 关键字。这些保留字不能用于常量、变量、和任何标识符的名称。

类别 关键字
private 私有的
protected 受保护的
public 公共的
default 默认
abstract 声明抽象
class
extends 扩充、继承
final 最终值、不可改变的
implements 实现(接口)
interface 接口
native 本地、原生方法(非 Java 实现)
new 创建
static 静态
strictfp 严格浮点、精准浮点
synchronized 线程、同步
transient 短暂
volatile 易失
break 跳出循环
case 定义一个值以供 switch 选择
continue 继续
do 运行
else 否则
for 循环
if 如果
instanceof 实例
return 返回
switch 根据值选择执行
while 循环
assert 断言表达式是否为真
catch 捕捉异常
finally 有没有异常都执行
throw 抛出一个异常对象
throws 声明一个异常可能被抛出
try 捕获异常
import 引入
package
boolean 布尔型
byte 字节型
char 字符型
double 双精度浮点
float 单精度浮点
int 整型
long 长整型
short 短整型
super 父类、超类
this 本类
void 无返回值
goto 是关键字,但不能使用
const 是关键字,但不能使用

**注意:**Java 的 null 不是关键字,类似于 true 和 false,它是一个字面常量,不允许作为标识符使用。

变量类型

整型

byte 1字节

short 2字节

int 4字节

long 8字节

浮点型

float 4字节

double 8字节

//float 常量定义需要在数的尾部加上F/f

注意:浮点型比较的时候需要使用API接口进行比较,精度上无法准确判断两者是否相等

API接口构成以及数据转化类型

image-20230717210557554

image-20230717210738882

1
2
3
4
5
6
7
8
//细节 1: 有多种类型的数据混合运算时,
//系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算
//细节 2: 当我们把精度(容量)大 的数据类型赋值给精度(容量)小 的数据类型时,
//就会报错,反之就会进行自动类型转换
//细节 3: (byte, short) 和 char 之间不会相互自动转换
//当把具体数赋给 byte 时,(1)先判断该数是否在 byte 范围内
//细节 4: byte,short,char 他们三者可以计算,在计算时首先转换为 int 类型
//自动提升原则: 表达式结果的类型自动提升为 操作数中最大的

强制转化

自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换符 ( ),但可能造成 精度降低或溢出,格外要注意

String转化

1
2
3
4
5
6
7
8
9
int n1 = 100;
float f1 = 1.1F;
double d1 = 4.5;
boolean b1 = true;
String s1 = n1 + "";
String s2 = f1 + "";
String s3 = d1 + "";
String s4 = b1 + "";
System.out.println(s1 + " " + s2 + " " + s3 + " " + s4);

通过将其他数据+""转化成String

1
2
3
4
5
6
7
String s = "123";
int a = Integer.parseInt(s);
//在将 String 类型转成基本数据类型时, ,比如 我们可以把 "123" , 转成一个整数,但是不能把 "hello" 转成一个整数

//怎么把字符串转成字符 char -> 含义是指 把字符串的第一个字符得到
//解读 s5.charAt(0) 得到 s5 字符串的第一个字符 '1'
System.out.println(s5.charAt(0)

逻辑运算

常规的不看

特殊的如下

1
2
3
4
&&短路与:如果第一个条件为 false,则第二个条件不会判断,最终结果为 false,效率高
& 逻辑与:不管第一个条件是否为 false,第二个条件都要判断,效率低
||短路或:如果第一个条件为 true,则第二个条件不会判断,最终结果为 true,效率高
|逻辑或:不管第一个条件是否为 true,第二个条件都要判断,效率低

命名规则

1
2
3
4
5
6
7
8
1) 包名:多单词组成时所有字母都小写:aaa.bbb.ccc //比如 com.hsp.crm
2) 类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz [大驼峰]
比如: TankShotGame
3) 变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz [小
驼峰, 简称 驼峰法]
比如: tankShotGame
4) 常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
比如 :定义一个所得税率 TAX_RATE

用户输入

1
2
3
Scanner myScanner = new Scanner(System.in); //新建Scanner对象进行用户输入
char score = myScanner.next().charAt(1); //接收第二个字符
System.out.println(score);
1
2
3
Scanner中存在next()和nextLine()方法接收输入字符串
has* //合法判断输入
next* //接收合法输入

switch

只会接受符合 byte、 short 、int、 char, enum[枚举], String 这 6 种类型的变量,多个符合条件的使用穿透

1
2
3
4
5
6
switch(month) {
case 3:
case 4:
case 5:
System.out.println("这是春季");
break;

类与对象

内存

1
2
3
4
Java 内存的结构分析
1) 栈: 一般存放基本数据类型(局部变量)
2) 堆: 存放对象(Cat cat , 数组等)
3) 方法区:常量池(常量,比如字符串), 类加载信

递归(跳过)

经典汉诺塔,迷宫等(找时间复习)

重载

老样子

可变参数

1
2
java 允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。
就可以通过可变参数实现

用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*(数据类型... 形参)
public int sum(int... n){

}*/
调用传入数组或者(传统)多个参数
public class main {
public static void main(String[] args) {
T t = new T();
int a[] = {1,2,3};
t.f1(a);
}
}
class T {
public void f1(int... nums) {
System.out.println("长度=" + nums.length);
}
//细节: 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
public void f2(String str, double... nums) {
}
//细节: 一个形参列表中只能出现一个可变参数
//下面的写法是错的. // public void f3(int... nums1, double... nums2) {
// }
}

继承

1
2
3
4
5
6
7
8
9
10
11
1) 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
2) 子类必须调用父类的构造器, 完成父类的初始化 //会先初始化父类再初始化子类
3) 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
4) 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
5) super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)
6) super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
7) java 所有类都是 Object 类的子类, Object 是所有类的基类.
8) 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)
9) 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。
思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】
10) 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系

重写

1
2
3
//细节: 子类方法不能缩小父类方法的访问权限
//public > protected > 默认>private

方法的重写规则

  • 参数列表与被重写方法的参数列表必须完全相同。
  • 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
  • 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
  • 父类的成员方法只能被它的子类重写。
  • 声明为 final 的方法不能被重写。
  • 声明为 static 的方法不能被重写,但是能够被再次声明。
  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
  • 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
  • 构造方法不能被重写。
  • 如果不能继承一个类,则不能重写该类的方法。

多态

image-20230718111723719

1
2
Animal animal = new Cat()  //多态向上
Cat cat = (Cat) anima //多态向下

instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型,用于验证和调用特殊方法(配合多态)

equals方法

垃圾回收

1
2
3
4
5
6
7
8
System.gc();//主动调用垃圾回收器

//重写Finalize方法
@Override
protected void finalize() throws Throwable {
System.out.println("我们销毁 汽车" + name );
System.out.println("释放了某些资源...");
}

类变量

1
2
3
static变量是类中所有对象共享的
//说明:类变量是随着类的加载而创建,所以即使没有创建对象实例也可以访问
同样的类方法也可以如此

main,构造器与代码块

1
2
3
4
5
6
7
8
//(1) 可以把相同的语句,放入到一个代码块中,即可
//(2) 这样当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容
//(3) 代码块调用的顺序优先于构造器..
{
System.out.println("电影屏幕打开...");
System.out.println("广告开始...");
System.out.println("电影正是开始...");
};

image-20230718120846211

image-20230718121712078

image-20230718121800878

单例模式

1、懒汉式,线程不安全

**是否 Lazy 初始化:**是

**是否多线程安全:**否

**实现难度:**易

**描述:**这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。

实例

1
2
3
4
5
6
7
8
9
10
11
public class Singleton {  
private static Singleton instance;
private Singleton (){}

public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

接下来介绍的几种实现方式都支持多线程,但是在性能上有所差异。

2、懒汉式,线程安全

**是否 Lazy 初始化:**是

**是否多线程安全:**是

**实现难度:**易

**描述:**这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。

实例

1
2
3
4
5
6
7
8
9
10
public class Singleton {  
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

3、饿汉式

**是否 Lazy 初始化:**否

**是否多线程安全:**是

**实现难度:**易

**描述:**这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。

实例

1
2
3
4
5
6
7
public class Singleton {  
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}

4、双检锁/双重校验锁(DCL,即 double-checked locking)

**JDK 版本:**JDK1.5 起

**是否 Lazy 初始化:**是

**是否多线程安全:**是

**实现难度:**较复杂

**描述:**这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
getInstance() 的性能对应用程序很关键。

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton {  
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

5、登记式/静态内部类

**是否 Lazy 初始化:**是

**是否多线程安全:**是

**实现难度:**一般

**描述:**这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 3 种方式就显得很合理。

实例

1
2
3
4
5
6
7
8
9
public class Singleton {  
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

6、枚举

**JDK 版本:**JDK1.5 起

**是否 Lazy 初始化:**否

**是否多线程安全:**是

**实现难度:**易

**描述:**这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。

实例

1
2
3
4
5
public enum Singleton {  
INSTANCE;
public void whateverMethod() {
}
}

**经验之谈:**一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。

final

image-20230718172541108

抽象类

1
2
3
4
//抽象类不一定要包含 abstract 方法。也就是说,抽象类可以没有 abstract 方法
//还可以有实现的方法。
//abstract 只能修饰类和方法,不能修饰属性和其它的
//如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为 abstract 类

接口

image-20230718173814577

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface UsbInterface { //接口
//规定接口的相关方法,即规范...
public void start();
public void stop();
}
public class Camera implements UsbInterface{//实现接口,就是把接口方法实现
@Override
public void start() {
System.out.println("相机开始工作...");
}
@Override
public void stop() {
System.out.println("相机停止工作....");
}
}

image-20230718174055429

image-20230718174512505

接口的多态还是需要进行多态的上下行,并且多态具有传递现象,如果B接口继承了A接口,C类实现了B接口中继承的A接口方法,就相当于实现了A接口的方法

内部类和匿名类

内部类:在类的内部定义,想要访问内部类的话就进行实例化就行(需要从该类进行对象的调用,因为内部类不是显式的)

匿名类:重新定义类的方法和逻辑(相当于继承和重写接口,但是不会执行多次)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class main {
public static void main(String[] args) {
AnonymousDemo an = new AnonymousDemo();
an.createClass();
}
}
class Polygon {
public void display() {
System.out.println("在 Polygon 类内部");
}
}

class AnonymousDemo {
public void createClass() {

// 创建的匿名类继承了 Polygon 类
Polygon p1 = new Polygon() {
public void display() {
System.out.println("在匿名类内部。");
}
};
p1.display();
}
}

然后可以进行调用

枚举类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public enum Day {
MONDAY(1, "星期一", "各种不在状态"){
@Override
public Day getNext() {
return TUESDAY;
}
},
TUESDAY(2, "星期二", "依旧犯困"){
@Override
public Day getNext() {
return WEDNESDAY;
}
},
WEDNESDAY(3, "星期三", "感觉半周终于过去了"){
@Override
public Day getNext() {
return THURSDAY;
}
},
THURSDAY(4, "星期四", "期待这星期五"){
@Override
public Day getNext() {
return FRIDAY;
}
},
FRIDAY(5, "星期五", "感觉还不错"){
@Override
public Day getNext() {
return SATURDAY;
}
},
SATURDAY(6, "星期六", "感觉非常好"){
@Override
public Day getNext() {
return SUNDAY;
}
},
SUNDAY(7, "星期日", "感觉周末还没过够。。。"){
@Override
public Day getNext() {
return MONDAY;
}
};

Day(int index, String name, String value) {
this.index = index;
this.name = name;
this.value = value;
}

private int index;
private String name;
private String value;
public abstract Day getNext();

public int getIndex() {
return index;
}

public void setIndex(int index) {
this.index = index;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

}

能够自动构造出变量的初始化以及自由的扩展度

常用类

包装类

包装类通过对八种基本类型相应的引用类型,使得八种数据基本类型具有了类的特点

int <-> Integer

对于两者之间的转化

1
2
3
4
int n1 = 100;
Integer integer = new Integer(n1);
Integer integer1 = Integer.valueOf(n1);
int i = integer.intValue();

注意点

1
2
3
4
Object obj1 = true? new Integer(1):new Double(2.0);
System.out.println(obj1);
//该代码将会输出1.0
三元运算符是一个整体,会将精度提升到最高,所以是1.0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Integer和int的比较

1)由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.print(i == j); //false

2)Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动将Integer拆箱为int,然后进行比较,实际上就变为两个int变量的比较)
Integer i = new Integer(1);
int j = 1;
System.out.print(i == j); //true

3)非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。(因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同)
Integer i = new Integer(1);
Integer j = 1;
System.out.print(i == j); //false

4)对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false
Integer i = 110;
Integer j = 110;
System.out.print(i == j); //true(因为-128到127java已经进行了缓存,所以i和j指向的是同一块内存,即同样的引用地址)
Integer i = 128;
Integer j = 128;
System.out.print(i == j); //false
java在编译Integer i = 110 时,会翻译成为Integer i = Integer.valueOf(110);而java API中对Integer类型的valueOf的定义如下:
public static Integer valueOf(int i){
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high){
return IntegerCache.cache[i + (-IntegerCache.low)];
}
return new Integer(i);
}
java对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了

String

image-20230719211826900

image-20230719211903915

image-20230720160752297

image-20230720160813039

String <-> StringBuffer

1
2
3
4
5
6
7
8
9
10
11
12
13
String str = "hello tom";
//方式 1 使用构造器
//注意: 返回的才是 StringBuffer 对象,对 str 本身没有影响
StringBuffer stringBuffer = new StringBuffer(str);
//方式 2 使用的是 append 方法
StringBuffer stringBuffer1 = new StringBuffer();
stringBuffer1 = stringBuffer1.append(str);
//看看 StringBuffer ->String
StringBuffer stringBuffer3 = new StringBuffer("韩顺平教育");
//方式 1 使用 StringBuffer 提供的 toString 方法
String s = stringBuffer3.toString();
//方式 2: 使用构造器来搞定
String s1 = new String(stringBuffer3)

StringBuffer 类常见方法

1
2
3
4
5
6
7
8
9
10
11
12
13
//增
s.append(',');// "hello,"
//删
s.delete(11, 14);
//改
s.replace(9, 11, "周芷若");
//查找指定的子串在字符串第一次出现的索引,如果找不到返回-1
int indexOf = s.indexOf("张三丰");
System.out.println(indexOf);//6
//插
s.insert(9, "赵敏");
//长度
System.out.println(s.length());
1
2
3
4
5
//1. StringBuffer 的直接父类 是 AbstractStringBuilder
//2. StringBuffer 实现了 Serializable, 即 StringBuffer 的对象可以串行化
//3. 在父类中 AbstractStringBuilder 有属性 char[] value,不是 fina
//4. StringBuffer 是一个 final 类,不能被继承
//5. 因为 StringBuffer 字符内容是存在 char[] value, 所有在变化(增加/删除
1
2
3
4
5
6
7
//1. StringBuilder 继承 AbstractStringBuilder 类
//2. 实现了 Serializable ,说明 StringBuilder 对象是可以串行化(对象可以网络传输,可以保存到文件)
//3. StringBuilder 是 final 类, 不能被继承
//4. StringBuilder 对象字符序列仍然是存放在其父类 AbstractStringBuilder 的 char[] value;
// 因此,字符序列是堆中
//5. StringBuilder 的方法,没有做互斥的处理,即没有 synchronized 关键字,因此在单线程的情况下使用
// StringBuilder

image-20230720215917270

image-20230720215933356

Arrays

1
2
3
4
5
6
1、Arrays.toString()打印数组
2、Arrays.equals(int[] a, int[] a2)比较两个数组是否相同
3、Arrays.copyOf(int[] original, int newLength)复制指定的数组---效率低,会重新开辟新的数组空间original - 要复制的数组;newLength - 要返回的副本的长度
4、Arrays.fill(int[] a, int val)/Arrays.fill(int[] a, int fromIndex, int toIndex, int val)填充数组
5、Arrays.sort(int[] a);对数组进行升序排序
6、Arrays.binarySearch(int[] a, int key)二分法查找

System

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1、拷贝数组arraycopy
public static void main(String[] args) {
int[] arr1 = {1,2,3,4,5 };
int[] arr2 = { 6,7,8,9,10};
/*
* 第一个参数arr1:被复制的数组
* 第二个参数1:arr1中要复制的起始位置
* 第三个参数arr2:目标数组
* 第四个参数0:目标数组的复制起始位置
* 第五个参数3:目标数组的复制结束位置
*/
System.arraycopy(arr1, 1, arr2, 0, 3);
for (int i = 0; i < 5; i++)
System.out.print(arr2[i] + " ");
}
2、获取系统时间
System.out.println(System.currentTimeMillis());
System.out.println(System.nanoTime());
3、垃圾回收相关操作:System.gc

BigInteger 和 BigDecimal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//当我们编程中,需要处理很大的整数,long 不够用
//可以使用 BigInteger
BigInteger bigInteger = new BigInteger("23788888899999999999999999999");
BigInteger add = bigInteger.add(bigInteger2);
System.out.println(add);//
BigInteger subtract = bigInteger.subtract(bigInteger2);
System.out.println(subtract);//减
BigInteger multiply = bigInteger.multiply(bigInteger2);
System.out.println(multiply);//乘
BigInteger divide = bigInteger.divide(bigInteger2);
System.out.println(divide);//除

//当我们需要保存一个精度很高的数时,double 不够用
//可以是 BigDecimal
//1. 如果对 BigDecimal 进行运算,比如加减乘除,需要使用对应的方法
//2. 创建一个需要操作的 BigDecimal 然后调用相应的方法即可
System.out.println(bigDecimal.add(bigDecimal2));
System.out.println(bigDecimal.subtract(bigDecimal2));
System.out.println(bigDecimal.multiply(bigDecimal2));
//System.out.println(bigDecimal.divide(bigDecimal2));//可能抛出异常 ArithmeticException
//在调用 divide 方法时,指定精度即可. BigDecimal.ROUND_CEILING
//如果有无限循环小数,就会保留 分子 的精度

时间

第一代
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Date d1 = new Date(); //获取当前系统时间
System.out.println("当前日期=" + d1);
Date d2 = new Date(9234567); //通过指定毫秒数得到时间
System.out.println("d2=" + d2); //获取某个时间对应的毫秒数
//1. 创建 SimpleDateFormat 对象,可以指定相应的格式
//2. 这里的格式使用的字母是规定好,不能乱写
SimpleDateFormat sdf = new SimpleDateFormat("yyyy 年 MM 月 dd 日 hh:mm:ss E");
String format = sdf.format(d1); // format:将日期转换成指定格式的字符串
System.out.println("当前日期=" + format);

//1. 可以把一个格式化的 String 转成对应的 Date
//2. 得到 Date 仍然在输出时,还是按照国外的形式,如果希望指定格式输出,需要转换
//3. 在把 String -> Date , 使用的 sdf 格式需要和你给的 String 的格式一样,否则会抛出转换异常
String s = "1996 年 01 月 01 日 10:20:30 星期一";
Date parse = sdf.parse(s);
System.out.println("parse=" + sdf.format(parse));
第二代
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//1. Calendar 是一个抽象类, 并且构造器是 private
//2. 可以通过 getInstance() 来获取实例
//3. 提供大量的方法和字段提供给程序员
//4. Calendar 没有提供对应的格式化的类,因此需要程序员自己组合来输出(灵活)
//5. 如果我们需要按照 24 小时进制来获取时间, Calendar.HOUR ==改成=> Calendar.HOUR_OF_DAY
Calendar c = Calendar.getInstance(); //创建日历类对象//比较简单,自由
System.out.println("c=" + c);
//2.获取日历对象的某个日历字段
System.out.println("年:" + c.get(Calendar.YEAR));
// 这里为什么要 + 1, 因为 Calendar 返回月时候,是按照 0 开始编号
System.out.println("月:" + (c.get(Calendar.MONTH) + 1));
System.out.println("日:" + c.get(Calendar.DAY_OF_MONTH));
System.out.println("小时:" + c.get(Calendar.HOUR));
System.out.println("分钟:" + c.get(Calendar.MINUTE));
System.out.println("秒:" + c.get(Calendar.SECOND));
//Calender 没有专门的格式化方法,所以需要程序员自己来组合显示
System.out.println(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-" +
c.get(Calendar.DAY_OF_MONTH) +
" " + c.get(Calendar.HOUR_OF_DAY) + ":" + c.get(Calendar.MINUTE) + ":" + c.get(Calendar.SECOND) );
第三代
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//1. 使用 now() 返回表示当前日期时间的 对象
LocalDateTime ldt = LocalDateTime.now(); //LocalDate.now();//LocalTime.now()
System.out.println(ldt);
//2. 使用 DateTimeFormatter 对象来进行格式化
// 创建 DateTimeFormatter 对象
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String format = dateTimeFormatter.format(ldt);
System.out.println("格式化的日期=" + format);
System.out.println("年=" + ldt.getYear());
System.out.println("月=" + ldt.getMonth());
System.out.println("月=" + ldt.getMonthValue());
System.out.println("日=" + ldt.getDayOfMonth());
System.out.println("时=" + ldt.getHour());
System.out.println("分=" + ldt.getMinute());
System.out.println("秒=" + ldt.getSecond());
LocalDate now = LocalDate.now(); //可以获取年月日
LocalTime now2 = LocalTime.now();//获取到时分秒
//提供 plus 和 minus 方法可以对当前时间进行加或者减
//看看 890 天后,是什么时候 把 年月日-时分秒
LocalDateTime localDateTime = ldt.plusDays(890);
System.out.println("890 天后=" + dateTimeFormatter.format(localDateTime));
//看看在 3456 分钟前是什么时候,把 年月日-时分秒输出
LocalDateTime localDateTime2 = ldt.minusMinutes(3456);
System.out.println("3456 分钟前 日期=" + dateTimeFormatter.format(localDateTime2));
时间戳
1
2
3
4
5
6
7
//1.通过 静态方法 now() 获取表示当前时间戳的对象
Instant now = Instant.now();
System.out.println(now);
//2. 通过 from 可以把 Instant 转成 Date
Date date = Date.from(now);
//3. 通过 date 的 toInstant() 可以把 date 转成 Instant 对象
Instant instant = date.toInstant()

集合

迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Collection col = new ArrayList();
col.add(new Book("三国演义", "罗贯中", 10.1));
col.add(new Book("小李飞刀", "古龙", 5.1));
col.add(new Book("红楼梦", "曹雪芹", 34.6));
//System.out.println("col=" + col);
//现在老师希望能够遍历 col 集合
//1. 先得到 col 对应的 迭代器
Iterator iterator = col.iterator();
//2. 使用 while 循环遍历
// while (iterator.hasNext()) {//判断是否还有数据
// //返回下一个元素,类型是 Object
// Object obj = iterator.next();
// System.out.println("obj=" + obj);
// }
//老师教大家一个快捷键,快速生成 while => itit
//显示所有的快捷键的的快捷键 ctrl + j
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("obj=" + obj);
}
//3. 当退出 while 循环后 , 这时 iterator 迭代器,指向最后的元素
// iterator.next();//NoSuchElementException
//4. 如果希望再次遍历,需要重置我们的迭代器
iterator = col.iterator();
System.out.println("===第二次遍历===");
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("obj=" + obj);
}

List接口实现

Collection接口常用方法
1
2
3
4
5
6
7
8
9
10
11
1. public boolean add(E e);  //添加元素到集合
2. public boolean addAll(Collection<? extends E> c); //存放一个集合
3. public boolean contains(Object o); //查找集合中的元素
4. public boolean isEmpty(); //判断一个集合是否为空
5. public boolean remove(Object 0);//删除一个集合中的元素
6. public int size();//返回集合的长度

List接口拓展了Collection接口中的方法
1. public E get(int index); //根据索引取得元素
2. public E set(int index,E element);//替换元素,index为要替换元素下标 element为要替换元素
3. public ListIterator<E> listIterator() List //List自己的迭代器
ArrayList
1
2
ArrayList<Integer> list = new ArrayList<Integer>();
List<String> list = new ArrayList<>();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
List list = new ArrayList();
for (int i = 0; i < 12; i++) {
list.add("hello" + i);
}
System.out.println("list=" + list);
//在 2 号位插入一个元素"韩顺平教育"
list.add(1, "韩顺平教育");
System.out.println("list=" + list);
//获得第 5 个元素
System.out.println("第五个元素=" + list.get(4));
//删除第 6 个元素
list.remove(5);
System.out.println("list=" + list);
//修改第 7 个元素
list.set(6, "三国演义");
System.out.println("list=" + list);
//在使用迭代器遍历集合
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("obj=" + obj);
}
1
2
3
Vector vector = new Vector(8);
LinkedList linkedList = new LinkedList();
LinkedList<String> link = new LinkedList<String>();

image-20230721160352586

set接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//1. 以 Set 接口的实现类 HashSet 来讲解 Set 接口的方法
//2. set 接口的实现类的对象(Set 接口对象), 不能存放重复的元素, 可以添加一个 null
//3. set 接口对象存放数据是无序(即添加的顺序和取出的顺序不一致)
//4. 注意:取出的顺序的顺序虽然不是添加的顺序,但是他的固定. Set set = new HashSet();
set.add("john");
set.add("lucy");
set.add("john");//重复
set.add("jack");
set.add("hsp");
set.add("mary");
set.add(null);//
set.add(null);//再次添加 nul

LinkedHashSet<Integer> lhs = new LinkedHashSet<>();
lhs.add(29);
lhs.add(29);
lhs.add(30);
lhs.add(21);

for (Integer integer : lhs) {
System.out.println(integer);
}


public class SetDemo {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 注:总分相同等情况下按照语文成绩排序,其次是数学成绩、英语成绩、年龄、姓名
double num1 = s1.getTotalScore() - s2.getTotalScore();
double num2 = (num1 == 0) ? s1.getChineseScore() - s2.getChineseScore() : num1;
double num3 = (num2 == 0) ? s1.getMathScore() - s2.getMathScore() : num2;
double num4 = (num3 == 0) ? s1.getEnglishScore() - s2.getEnglishScore() : num3;
double num5 = (num4 == 0) ? s1.getAge() - s2.getAge() : num4;
double num6 = (num5 == 0) ? s1.getName().compareTo(s2.getName()) : num5;
return (num6 < 0) ? -1 : ( (num6 == 0) ? 0 : 1 );
}
});
for (int i = 0; i < 10; i++) {
String name = (char)(i+97) + "zhangsan";
int age = getRandomNum(18, 20);
double chineseScore = getRandomNum(88, 89);
double mathScore = getRandomNum(90, 91);
double englishScore = getRandomNum(95, 96);

Student s = new Student(name, age, chineseScore, mathScore, englishScore);
ts.add(s);
}

for (Student s : ts) {
System.out.println(s);
}
}
public static int getRandomNum(int start, int end) {
return (int) (Math.random() * (end - start + 1) + start);
}
}

Map接口实现

HashMap与LinkedHashMap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
int size();//获取Map集合大小(即元素数量)
boolean isEmpty();//判断是否为空
boolean containsKey(Object key);//判断是否包含某个键
boolean containsValue(Object value);//判断是否包含某个值
V get(Object key);//获取某个键对应的值
V put(K key, V value);//添加键值对(K,V)
V remove(Object key);//移除某个键对应的键值对
void putAll(Map<? extends K, ? extends V> m);//添加另一个Map集合
void clear();//清空所有键值对
Set<K> keySet();//获取键的集合
Collection<V> values();//获取值的集合
Set<Map.Entry<K, V>> entrySet();//获取键值对实体的集合
interface Entry<K,V>//Map中的内部接口



HashMap<Student, String> hm = new HashMap<Student, String>();
hm.put(new Student("2018050401", "张三", 18, 80.0),"2018050401");
hm.put(new Student("2018050402", "李四", 18, 80.0),"2018050402");
hm.put(new Student("2018050403", "李四", 18, 80.0), "2018050403");
hm.put(new Student("2018050404", "王五", 18, 80.0), "2018050404");
hm.put(new Student("2018050404", "王五", 18, 80.0), "2018050404");

// 方式二: 通过键值对对象找键找值
Set<Entry<Student, String>> keyValues = hm.entrySet();
for (Entry<Student, String> keyValue : keyValues) {
Student s = keyValue.getKey();
String value = keyValue.getValue();
System.out.println(s.getId() + "|" + s.getName() + "|" + s.getAge() + "|" + s.getScore() + "=" + value);
}


LinkedHashMap<Integer, String> lhm = new LinkedHashMap<Integer, String>();
lhm.put(01, "张三1");
lhm.put(02, "张三2");
lhm.put(03, "张三3");
lhm.put(04, "张三4");
lhm.put(05, "张三5");

Set<Integer> keys = lhm.keySet();
for (Integer key : keys) {
System.out.println(key + "|" + lhm.get(key));
}


TreeMap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
public class TreeMapTest {
public static void main(String[] agrs){
//创建TreeMap对象:
TreeMap<String,Integer> treeMap = new TreeMap<String,Integer>();
System.out.println("初始化后,TreeMap元素个数为:" + treeMap.size());

//新增元素:
treeMap.put("hello",1);
treeMap.put("world",2);
treeMap.put("my",3);
treeMap.put("name",4);
treeMap.put("is",5);
treeMap.put("jiaboyan",6);
treeMap.put("i",6);
treeMap.put("am",6);
treeMap.put("a",6);
treeMap.put("developer",6);
System.out.println("添加元素后,TreeMap元素个数为:" + treeMap.size());

//遍历元素:
Set<Map.Entry<String,Integer>> entrySet = treeMap.entrySet();
for(Map.Entry<String,Integer> entry : entrySet){
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println("TreeMap元素的key:"+key+",value:"+value);
}

//获取所有的key:
Set<String> keySet = treeMap.keySet();
for(String strKey:keySet){
System.out.println("TreeMap集合中的key:"+strKey);
}

//获取所有的value:
Collection<Integer> valueList = treeMap.values();
for(Integer intValue:valueList){
System.out.println("TreeMap集合中的value:" + intValue);
}

//获取元素:
Integer getValue = treeMap.get("jiaboyan");//获取集合内元素key为"jiaboyan"的值
String firstKey = treeMap.firstKey();//获取集合内第一个元素
String lastKey =treeMap.lastKey();//获取集合内最后一个元素
String lowerKey =treeMap.lowerKey("jiaboyan");//获取集合内的key小于"jiaboyan"的key
String ceilingKey =treeMap.ceilingKey("jiaboyan");//获取集合内的key大于等于"jiaboyan"的key
SortedMap<String,Integer> sortedMap =treeMap.subMap("a","my");//获取集合的key从"a"到"jiaboyan"的元素

//删除元素:
Integer removeValue = treeMap.remove("jiaboyan");//删除集合中key为"jiaboyan"的元素
treeMap.clear(); //清空集合元素:

//判断方法:
boolean isEmpty = treeMap.isEmpty();//判断集合是否为空
boolean isContain = treeMap.containsKey("jiaboyan");//判断集合的key中是否包含"jiaboyan"
}
}


public class SortedTest {
private int age;
public SortedTest(int age){
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class SortedTestComparator implements Comparator<SortedTest> {
//自定义比较器:实现compare(T o1,T o2)方法:
public int compare(SortedTest sortedTest1, SortedTest sortedTest2) {
int num = sortedTest1.getAge() - sortedTest2.getAge();
if(num==0){//为0时候,两者相同:
return 0;
}else if(num>0){//大于0时,后面的参数小:
return 1;
}else{//小于0时,前面的参数小:
return -1;
}
}
}

public class TreeMapTest {
public static void main(String[] agrs){
//自定义顺序比较
customSort();
}
//自定义排序顺序:
public static void customSort(){
TreeMap<SortedTest,String> treeMap = new TreeMap<SortedTest, String>(new SortedTestComparator());
treeMap.put(new SortedTest(10),"hello");
treeMap.put(new SortedTest(21),"my");
treeMap.put(new SortedTest(15),"name");
treeMap.put(new SortedTest(2),"is");
treeMap.put(new SortedTest(1),"jiaboyan");
treeMap.put(new SortedTest(7),"world");
System.out.println(treeMap.toString());
}
}

image-20230721171409085