常用类

2020/1/1

# 常用类的使用

# Object类的使用

# Object类的特点

  1. Object类是所有Java类的根父类.

  2. 如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类

    public class Person {}
    //等价于
    public class Person extends Object {}
    
    1
    2
    3
  3. Object类中的功能(属性、方法)就具有通用性。

    • 属性:无
    • 方法:equals() / toString() / getClass() /hashCode() / clone() / finalize()wait() 、 notify()、notifyAll()等
  4. Object类只声明了一个空参的构造器

# Object类的主要结构

# toString()的使用

  1. 当我们输出一个对象的引用时,实际上就是调用当前对象的toString()

  2. Object类中toString()的定义:

    toString()方法在Object类中定义, 其返回值是String类型, 返回类名和它的引用地址。

    public String toString() {
    	return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    
    1
    2
    3
  3. 在进行String与其它类型数据的连接操作时, 自动调用toString()方法

    Date now=new Date();
    System.out.println(“now=+now); 
    //相当于
    System.out.println(“now=+now.toString());
    
    1
    2
    3
    4
  4. 像String、Date、File、包装类等都重写了Object类中的toString()方法。使得在调用对象的toString()时,返回"实体内容"信息

  5. 基本类型数据转换为String类型时, 调用了对应包装类的toString()方法

    int a=10; 
    System.out.println(“a=+a);
    
    1
    2
  6. 自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"。

public class Customer {
	
	private String name;
	private int age;
	
	//getter和setter略

	//自动实现
	@Override
	public String toString() {
		return "Customer [name=" + name + ", age=" + age + "]";
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# equals()的使用(== 和 equals() 区别)

# “==” 的使用

== 为运算符

  1. 可以使用在基本数据类型变量和引用数据类型变量中
  2. 如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等(不一定类型要相同)。如果比较的是引用数据类型变量:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体

补充: == 符号使用时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外), 否则编译出错

# equals()方法的使用
  1. 是一个方法,而非运算符

  2. 只能适用于引用数据类型

  3. Object类中equals()的定义:

    public boolean equals(Object obj) {
          return (this == obj);
    }
    
    1
    2
    3

    说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体

  4. 像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。

  5. 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们就需要对Object类中的equals()进行重写.

    重写的原则:比较两个对象的实体内容是否相同.

# 重写equals()方法的原则
  • 对称性: 如果x.equals(y)返回是“true” , 那么y.equals(x)也应该返回是“true” 。

  • 自反性: x.equals(x)必须返回是“true” 。

  • 传递性: 如果x.equals(y)返回是“true” , 而且y.equals(z)返回是“true” ,那么z. equals(x)也应该返回是“true” 。

  • 一致性: 如果x.equals(y)返回是“true” , 只要x和y内容一直不变, 不管你重复x.equals(y)多少次, 返回都是“true” 。

  • 任何情况下, x.equals(null), 永远返回是“false” ;x.equals(和x不同类型的对象)永远返回是“false” 。

# 重写示例:
public class Customer {
	
	private String name;
	private int age;
	
	//getter和setter略

	//自动生成的equals()
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Customer other = (Customer) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

}
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
# equals()使用示例
public class EqualsTest {
	public static void main(String[] args) {
		
		//基本数据类型
		int i = 10;
		int j = 10;
		double d = 10.0;
		System.out.println(i == j);//true
		System.out.println(i == d);//true
		
		boolean b = true;
//		System.out.println(i == b);
		
		char c = 10;
		System.out.println(i == c);//true
		
		char c1 = 'A';
		char c2 = 65;
		System.out.println(c1 == c2);//true
		
		//引用类型:
		Customer cust1 = new Customer("Tom",21);
		Customer cust2 = new Customer("Tom",21);
		System.out.println(cust1 == cust2);//false
		
		String str1 = new String("atguigu");
		String str2 = new String("atguigu");
		System.out.println(str1 == str2);//false
		System.out.println("****************************");
		System.out.println(cust1.equals(cust2));//false--->true
		System.out.println(str1.equals(str2));//true
		
		Date date1 = new Date(32432525324L);
		Date date2 = new Date(32432525324L);
		System.out.println(date1.equals(date2));//true
				
	}
}
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
# 小结
  1. == 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
  2. equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也是==;我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中用的比较多,久而久之,形成了equals是比较值的错误观点。
  3. 具体要看自定义类里有没有重写Object的equals方法来判断。
  4. 通常情况下,重写equals方法,会比较类中的相应属性是否都相等。

# 包装类

针对八种基本数据类型定义相应的引用类型—包装类(封装类) 有了类的特点,就可以调用类中的方法, Java才是真正的面向对象

# 包装类与基本数据类型对照表

image-20200601223357534

# 基本数据类型与包装类的相互转换

# 基本数据类型包装成包装类的实例(装箱)

  1. 通过包装类的构造器实现:

    int i = 500;
    Integer t = new Integer(i);
    
    1
    2
  2. 通过字符串参数构造包装类对象:

    Float f = new Float(4.56);
    
    1
# 获得包装类对象中包装的基本类型变量 ---拆箱

调用包装类的.xxxValue()方法:

boolean b = Obj.booleanValue();
1

JDK1.5之后,支持自动装箱,自动拆箱。但类型必须匹配。

# 自动装箱与拆箱

# 装箱

装箱:包装类使得一个基本数据类型的数据变成了类。有了类的特点,可以调用类中的方法。

# 装箱原理(调用包装类的构造器)
int i = 500;
Integer t = new Integer(i);
1
2
# 拆箱

拆箱:将包装类中内容变为基本数据类型

# 拆箱原理(调用包装类Xxx的xxxValue())
Integer t = new Integer(500);
int j = t.intValue(); // j = 500, intValue取出包装类中的数据
1
2
# 自动装箱与拆箱

JDK1.5之后,支持自动装箱,自动拆箱。但类型必须匹配。即包装类与基本数据类型可以直接赋值,不必再调用构造器或者xxxValue()。

# 示例
//自动装箱:基本数据类型 --->包装类
		int num2 = 10;
		Integer in1 = num2;//自动装箱
		
		boolean b1 = true;
		Boolean b2 = b1;//自动装箱
		
		//自动拆箱:包装类--->基本数据类型
		System.out.println(in1.toString());
		
		int num3 = in1;//自动拆箱
1
2
3
4
5
6
7
8
9
10
11

# 基本数据类型、包装类、String三者之间的相互转换

基本数据类型与包装类互相转换上文相当于上文的装箱和拆箱的原理,不在阐述。

包装类在实际开发中用的最多的在于字符串变为基本数据类型。

# String类型 --->基本数据类型、包装类:调用包装类的parseXxx(String s)
String str1 = "123";
int num2 = Integer.parseInt(str1);
System.out.println(num2 + 1);
		
String str2 = "true1";
boolean b1 = Boolean.parseBoolean(str2);
System.out.println(b1);
1
2
3
4
5
6
7
# 基本数据类型、包装类--->String类型:调用String重载的valueOf(Xxx xxx)或者做连接运算
  1. 连接运算

    int num1 = 10;
    String str1 = num1 + "";
    
    1
    2
  2. 调用String重载的valueOf(Xxx xxx)

    float f1 = 12.3f;
    String str2 = String.valueOf(f1);//"12.3"
    
    Double d1 = new Double(12.4);
    String str3 = String.valueOf(d1);
    System.out.println(str2);
    System.out.println(str3);//"12.4"
    
    1
    2
    3
    4
    5
    6
    7
# 图解基本数据类型、包装类、String三者之间的相互转换
image-20200602083207150

# 思考题

# 1.思考下面两种方式输出的值为什么不同?

o1输出为1.0

Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1);//1.0
1
2

o1输出为1

Object o2;
if (true)
o2 = new Integer(1);
else
o2 = new Double(2.0);
System.out.println(o2);//1
1
2
3
4
5
6
# 思考

因为三元运算操作需要保证?后的两个表达式的数据类型相同,int需要向上转型为double

# 2.比较下列的值是否相同
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);//false

//Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[],
//保存了从-128~127范围的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在
//-128~127范围内时,可以直接使用数组中的元素,不用再去new了。目的:提高效率

Integer m = 1;
Integer n = 1;
System.out.println(m == n);//true

Integer x = 128;//相当于new了一个Integer对象
Integer y = 128;//相当于new了一个Integer对象
System.out.println(x == y);//false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 抽象类与抽象方法

# 修饰对象

abstract可以用来修饰的结构:类、方法

# 注意事项

  1. 不能用abstract修饰变量、代码块、构造器;
  2. 不能用abstract修饰私有方法、静态方法、 final的方法、 final的类。

# 抽象类

随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。

抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提供具体实现的对象的类。

abstract关键字来修饰一个类, 这个类叫做抽象类。

# 抽象类的特点

  1. 抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。

  2. 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)

  3. 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作

# 抽象方法

用abstract来修饰一个方法, 该方法叫做抽象方法。只有方法的声明,没有方法的实现以分号结束。

# 抽象方法的特点

  1. 抽象方法只有方法的声明,没有方法体

  2. 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。

  3. 若子类重写了父类中的所有的抽象方法后,此子类方可实例化

  4. 若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰

# 示例

# 抽象类的使用示例

abstract class A { 
    
    abstract void m1();
    
    public void m2() {
    System.out.println("A类中定义的m2方法");
    }
    
}

class B extends A {
    
    void m1() {
    System.out.println("B类中定义的m1方法");
    }
}

public class Test {
    public static void main(String args[]) {
        A a = new B();
        a.m1();
        a.m2();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 接口

接口(interface)是抽象方法和常量值定义的集合。

Java中,接口和类是并列的两个结构

# 为什么使用接口?

  1. 一方面, 有时必须从几个类中派生出一个子类, 继承它们所有的属性和方法。 但是, Java不支持多重继承。 有了接口, 就可以得到多重继承的效果。
  2. 另一方面, 有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、 MP3机、手机、数码相机、移动硬盘等都支持USB连接。

# 接口的特点

  1. 用interface来定义。
  2. 接口中的所有成员变量都默认是由public static final修饰的。
  3. 接口中的所有抽象方法都默认是由public abstract修饰的。
  4. 接口中没有构造器!意味着接口不可以实例化
  5. 接口采用多继承机制。
  6. 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。
  7. 与继承关系类似,接口与实现类之间存在多态性
  8. 接口和类是并列关系, 或者可以理解为一种特殊的类。 从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义(JDK7.0及之前), 而没有变量和方法的实现(JDK8后拥有)。

# 如何定义接口?

# JDK7以前

只能定义全局常量和抽象方法

全局常量:public static final的.但是书写时,可以省略不写

抽象方法:public abstract的

# JDK8

除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)

# 接口实现VS类继承

  1. 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则必须能...”的思想。 继承是一个"是不是"的关系,而接口实现则是 "能不能"的关系。
  2. 接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。

# 图例

image-20200604181227344

# 接口VS抽象类

image-20200604182003689

在开发中,常看到一个类不是去继承一个已经实现好的类,而是要么继承抽象类,要么实现接口。

# 接口的使用示例

/*
 * 接口的使用
 * 1.接口使用上也满足多态性
 * 2.接口,实际上就是定义了一种规范
 * 3.开发中,体会面向接口编程!
 * 
 */
public class USBTest {
	public static void main(String[] args) {
		
		Computer com = new Computer();
		//1.创建了接口的非匿名实现类的非匿名对象
		Flash flash = new Flash();
		com.transferData(flash);
		
		//2. 创建了接口的非匿名实现类的匿名对象
		com.transferData(new Printer());
		
		//3. 创建了接口的匿名实现类的非匿名对象
		USB phone = new USB(){

			@Override
			public void start() {
				System.out.println("手机开始工作");
			}

			@Override
			public void stop() {
				System.out.println("手机结束工作");
			}
			
		};
		com.transferData(phone);
		
		
		//4. 创建了接口的匿名实现类的匿名对象
		
		com.transferData(new USB(){
			@Override
			public void start() {
				System.out.println("mp3开始工作");
			}

			@Override
			public void stop() {
				System.out.println("mp3结束工作");
			}
		});
	}
}

class Computer{
	
	public void transferData(USB usb){//USB usb = new Flash();
		usb.start();
		
		System.out.println("具体传输数据的细节");
		
		usb.stop();
	}
	
	
}

interface USB{
	//常量:定义了长、宽、最大最小的传输速度等
	
	void start();
	
	void stop();
	
}

class Flash implements USB{

	@Override
	public void start() {
		System.out.println("U盘开启工作");
	}

	@Override
	public void stop() {
		System.out.println("U盘结束工作");
	}
	
}

class Printer implements USB{
	@Override
	public void start() {
		System.out.println("打印机开启工作");
	}

	@Override
	public void stop() {
		System.out.println("打印机结束工作");
	}
	
}


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

# JDK8关于接口的改进

Java 8中,你可以为接口添加静态方法默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。

# 静态方法

使用 static 关键字修饰。 可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。

# 使用示例
interface Flyable{
	
	//全局常量
	public static final int MAX_SPEED = 7900;//第一宇宙速度
	int MIN_SPEED = 1;//省略了public static final
	
	//抽象方法
	public abstract void fly();
	//省略了public abstract
	void stop();

}
1
2
3
4
5
6
7
8
9
10
11
12

# 默认方法

默认方法使用 default 关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如: java 8 API中对Collection、 List、 Comparator等接口提供了丰富的默认方法。

# 使用示例
interface Filial {// 孝顺的
    default void help() {
    	System.out.println("老妈,我来救你了");
    }
}

interface Spoony {// 痴情的
    default void help() {
    	System.out.println("媳妇,别怕,我来了");
    }
}

class Man implements Filial, Spoony {
    @Override
    public void help() {
    	System.out.println("我该怎么办呢?");
    	Filial.super.help();
    	Spoony.super.help();
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 注意事项

  1. 如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错。会出现: 接口冲突。 解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突(在实现类中重写此方法)。
  2. 若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守: 类优先原则。 接口中具有相同名称和参数的默认方法会被忽略。(如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。)
  3. 接口中定义的静态方法,只能通过接口来调用。
  4. 通过实现类的对象,可以调用接口中的默认方法。如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法。

# String

  String类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。 String对象的字符内容是存储在一个字符数组value[]中的。 这里有个问题,既然String对象是字符串常量不可改变,那么我们经常对其进行多次赋值,那是怎么回事的呢?下面的例子是我们不能理解的

String s = "aaa";
s = "bbb";
// 打印出来的s为bbb
System.out.println(s);
1
2
3
4

原来,上面的对String进行重新赋值的操作会将值bbb赋值给生成新的String对象,而旧的对象值aaa则是等待回收。

String s = "ccc";
s = s +"ddd";
// 打印出来的s为cccddd
System.out.println(s)
1
2
3
4

上面的String字符串拼接,看似改变了原有的值,其实只是假象。真正的原理是: ①String.valueOf(str1) ②产生StringBuilder, 调用的StringBuilder(str1)构造方法, 把StringBuilder初始化,长度为str1.length()+16,并且调用append(str1) ③调用StringBuilder.append(str2), 把第二个字符串拼接进去, 然后调用StringBuilder.toString返回结果

//Java1.5前为StringBuffer,1.5之后为StringBuilder
StringBuilder.append("ccc").append("ddd").toString();
1
2

  String的字符串拼接是要额外占据内存的,以“hello”拼接“world”为例,短短的两个字符串,却需要开辟三次内存空间(极大浪费): image-20200726215027215

# String初始化情况

image-20200726215050975 image-20200726215116524

# String str1 = “abc”String str2 = new String(“abc”)的区别?

字符串常量存储在字符串常量池, 目的是共享 字符串非常量对象存储在堆中。

# String判断

image-20200726215200345 常量与常量的拼接结果在常量池。 且常量池中不会存在相同内容的常量。 只要其中有一个是变量, 结果就在堆中 如果拼接的结果调用intern()方法, 返回值就在常量池

# 注意事项
  1. String s1 = "a"; 说明:在字符串常量池中创建了一个字面量为"a"的字符串。
  2. s1 = s1 + "b"; 说明:实际上原来的“a”字符串对象已经丢弃了, 现在在堆空间中产生了一个字符串s1+"b"(也就是"ab")。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能。
  3. String s2 = "ab"; 说明:直接在字符串常量池中创建一个字面量为"ab"的字符串。
  4. String s3 = "a" + "b"; 说明: s3指向字符串常量池中已经创建的"ab"的字符串。
  5. String s4 = s1.intern(); 说明:堆空间的s1对象在调用intern()之后,会将常量池中已经存在的"ab"字符串赋值给s4。

# 常用方法

int length(): 返回字符串的长度: return value.length
char charAt(int index): 返回某索引处的字符return value[index]
boolean isEmpty(): 判断是否是空字符串: return value.length == 0
String toLowerCase(): 使用默认语言环境, 将 String 中的所有字符转换为小写
String toUpperCase(): 使用默认语言环境, 将 String 中的所有字符转换为大写
String trim(): 返回字符串的副本, 忽略前导空白和尾部空白
boolean equals(Object obj): 比较字符串的内容是否相同
boolean equalsIgnoreCase(String anotherString): 与equals方法类似, 忽略大小写
String concat(String str): 将指定字符串连接到此字符串的结尾。 等价于用“+”
int compareTo(String anotherString): 比较两个字符串的大小
String substring(int beginIndex): 返回一个新的字符串, 它是此字符串的从beginIndex开始截取到最后的一个子字符串。
String substring(int beginIndex, int endIndex) : 返回一个新字符串, 它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
boolean endsWith(String suffix): 测试此字符串是否以指定的后缀结束
boolean startsWith(String prefix): 测试此字符串是否以指定的前缀开始
boolean startsWith(String prefix, int toffset): 测试此字符串从指定索引开始的子字符串是否以指定前缀开始
boolean contains(CharSequence s): 当且仅当此字符串包含指定的 char 值序列时,返回 true
int indexOf(String str): 返回指定子字符串在此字符串中第一次出现处的索引
int indexOf(String str, int fromIndex): 返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
int lastIndexOf(String str): 返回指定子字符串在此字符串中最右边出现处的索引
int lastIndexOf(String str, int fromIndex): 返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索 
注: indexOf和lastIndexOf方法如果未找到都是返回-1
String replace(char oldChar, char newChar): 返回一个新的字符串, 它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
String replace(CharSequence target, CharSequence replacement): 使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
String replaceAll(String regex, String replacement) : 使 用 给 定 的replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
String replaceFirst(String regex, String replacement) : 使 用 给 定 的replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
boolean matches(String regex): 告知此字符串是否匹配给定的正则表达式。
String[] split(String regex): 根据给定正则表达式的匹配拆分此字符串。
 String[] split(String regex, int limit): 根据匹配给定的正则表达式来拆分此字符串, 最多不超过limit个, 如果超过了, 剩下的全部都放到最后一个元素中。

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

# 类型转换

  1. 字符串——>基本数据类型、包装类

    • Integer包装类的public static int parseInt(String s):可以将由“数字”字符组成的字符串转换为整型。
    • 类似地,使用java.lang包中的Byte、 Short、 Long、 Float、 Double类调相应的类方法可以将由“数字”字符组成的字符串,转化为相应的基本数据类型。
  2. 基本数据类型、包装类——> 字符串

    • 调用String类的public String valueOf(int n)可将int型转换为字符串
    • 相应的valueOf(byte b)、 valueOf(long l)、 valueOf(float f)、 valueOf(doubled)、 valueOf(boolean b)可由参数的相应类型到字符串的转换
  3. 字符数组——>字符串

String 类的构造器: String(char[]) 和 String(char[], int offset, int length) 分别用字符数组中的全部字符和部分字符创建字符串对象。

  1. 字符串——>字符数组
  • public char[] toCharArray(): 将字符串中的全部字符存放在一个字符数组中的方法。
  • public void getChars(int srcBegin, int srcEnd, char[] dst,int dstBegin): 提供了将指定索引范围内的字符串存放到数组中的方法。
  1. 字节数组——>字符串
  • String(byte[]): 通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
  • String(byte[], int offset, int length) : 用指定的字节数组的一部分,即从数组起始位置offset开始取length个字节构造一个字符串对象。
  1. 字符串——>字节数组
  • public byte[] getBytes() : 使用平台的默认字符集将此 String 编码为byte 序列,并将结果存储到一个新的 byte 数组中。
  • public byte[] getBytes(String charsetName) : 使用指定的字符集将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。

# StringBuffer

  StringBuffer对象则代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()等方法可以改变这个字符串对象的字符序列。一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象

StringBuffer b = new StringBuffer("123");
b.append("456");
// b打印结果为:123456
System.out.println(b);
1
2
3
4

b对象的内存空间图: image-20200726215214142   没有产生新的字符串,所以说StringBuffer对象是一个字符序列可变的字符串,它没有重新生成一个对象,而且在原来的对象中可以连接新的字符串。

# StringBuffer的常用方法:


StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接
StringBuffer delete(int start,int end):删除指定位置的内容
StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str
StringBuffer insert(int offset, xxx):在指定位置插入xxx
StringBuffer reverse() :把当前字符序列逆转
public int indexOf(String str)
public String substring(int start,int end):返回一个从start开始到end索引结束的左闭右开区间的子字符串
public int length()
public char charAt(int n )
public void setCharAt(int n ,char ch)

总结:
增:append(xxx)
删:delete(int start,int end)
改:setCharAt(int n ,char ch) / replace(int start, int end, String str)
查:charAt(int n )
插:insert(int offset, xxx)
长度:length();
*遍历:for() + charAt() / toString()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# StringBuilder

  StringBuilder类也代表可变字符串对象。实际上,StringBuilder和StringBuffer基本相似,两个类的构造器和方法也基本相同。不同的是:StringBuffer是线程安全的,而StringBuilder则没有实现线程安全功能,所以性能略高

# String、StringBuffer和StringBuilder的共同与区别之处

# 三者继承关系

image-20200726215227735

# 三者共同之处:

都是final类,不允许被继承,主要是从性能和安全性上考虑的,因为这几个类都是经常被使用着,且考虑到防止其中的参数被参数修改影响到其他的应用;底层都使用char[]存储

# 区别:

StringBuffer是线程安全,可以不需要额外的同步用于多线程中;

StringBuilder是非同步,运行于多线程中就需要使用着单独同步处理,但是速度就比StringBuffer快多了;

StringBuffer与StringBuilder两者共同之处:可以通过append、indert进行字符串的操作。

String实现了三个接口:Serializable、Comparable、CarSequence

StringBuilder只实现了两个接口Serializable、CharSequence,相比之下String的实例可以通过compareTo方法进行比较,其他两个不可以

# 三者区别之处:

  1. 运行速度(执行速度):StringBuilder > StringBuffer > String   Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢;而StringBuilder和StringBuffer的对象是变量,对变量进行操作就是直接对该对象进行更改,而不进行创建和回收的操作,所以速度要比String快很多。
  2. 线程安全:StringBuilder是线程不安全的,而StringBuffer是线程安全的   如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。

# 最优应用

  1. String:适用于少量的字符串操作的情况
  2. StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
  3. StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

# Java的比较器

# java.lang.Comparable接口

# Comparable接口

是一个给排序方法使用的接口, 这是一个自比较器接口。用于指定一个对象如何去比较大小。

# Comparable接口中的方法

int compareTo(T obj) 比较当前对象与传入进来对象的大小

参数:obj 表示要比较的对象返回值: *如果当前的对象大于obj,返回正整数。 *如果当前的对象等于obj,返回0 *如果当前对象小于obj,返回负整 这是Java的一种约定, 当排序的sort()方法在对集合中的元素两两进行比较的时候会调用我们实现的compareTo(T obj)方法。 如果compareTo()返回正整数,则表示当前元素大于和它比较的元素; 如果返回负整数则表示当前元素小于和它比较的元素; 如果返回0,则表示两者相等。

# 案例演示

# 案例需求:

在数组中有三个员工对象,员工包含三个属性:编号、姓名、工资。

我们使用Arrays.sort()方法对数组中的员工按工资从小到大排序。 案例分析:

  1. 创建员工实体类,包含属性int id,String name和int salary。

  2. 通过员工的构造方法传入三个属性,生成toString()方法,生成无参的构造方法。

  3. 员工实现Comparable接口,并且实现compareTo()方法:用当前员工工资减去另一个员工的工资

  4. 在main函数中创建三个元素的数组,并且创建三个员工,添加到数组中。

  5. 调用Arrays.sort()方法对员工进行排序

  6. 思考:如果要按工资的降序排序如何实现?如果要用id排序呢?

# 案例代码:
//如果要比较大小,必须实现Comparable接口
class Employee implements Comparable<Employee> {

    private int id;
    private String name;
    private int salary;

    @Override
    public int compareTo(Employee o) {  //告诉sort()方法比较规则
        return o.salary - this.salary;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Demo03Comparable {

    public static void main(String[] args) {

        Employee[] employees = {
                new Employee(1, "孙悟空",7000),
                new Employee(2, "孙悟天",5000),
                new Employee(3, "孙悟饭",6000),
        };

       System.out.println("排序前:");
        for (Employee employee : employees) {
            System.out.println(employee);
        }

        //排序
        Arrays.sort(employees);

        System.out.println("排序后:");
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }

}
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

# java.util.Comparator接口

Comparator接口 创建一个比较器的接口,一个类实现这个接口,相当于指定了一个排序的规则。

Comparator接口中的方法 int compare(T obj1, T obj2)
比较o1和o2这两个对象如果: obj1 > obj2,这个方法返回正整数 obj2 == obj1, 返回0 obj1 < obj2,这个方法返回负整数

# 案例演示

# 需求说明
  1. 定义一个学生类,成员变量有:姓名,年龄

  2. 创建多个学生对象添加到集合中

  3. 创建类Demo04Comparator按年龄从小到大对集合的学生对象进行排序

# 代码实现
class Student {
    private String name;
    private int age;
    private int score;
}

//年龄比较器
class AgeComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getAge() - o2.getAge();
    }
}
//成绩比较器
class ScoreComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getScore() - o2.getScore();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Demo04Comparator {
    public static void main(String[] args) {
        //2)    创建多个学生对象添加到集合中
        List<Student> students = new ArrayList<>();
        students.add(new Student("张三",20,86));
        students.add(new Student("李四",18,95));
        students.add(new Student("老王",24,67));

        System.out.println("排序前:");
        System.out.println(students);

        //3)    创建类Demo04Comparator按年龄从小到大对集合的学生对象进行排序
        Collections.sort(students, new AgeComparator());
        System.out.println("年龄排序后:");
        System.out.println(students);
        
        //按分数进行排序
        Collections.sort(students, new ScoreComparator());
        System.out.println("分数排序后:");
        System.out.println(students);
	}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# Comparable和Comparator的区别

Comparable & Comparator 都是用来实现集合中的排序的, 只是 Comparable 是在对象内部定义的方法实现的排序,Comparator 是在集合外部实现的排序。

  1. Comparable位于包java.lang下,Comparator位于包java.util下。 Comparable接口将比较代码嵌入自身类中,而Comparable在一个独立的类中实现比较。

  2. 如果类的设计师没有考虑到Compare的问题而没有实现Comparable接口, 可以通过Comparator 来实现比较算法进行排序。

  3. Comparator为了使用不同的排序规则做准备。比如:升序、降序或按不同的属性进行排序。

# 字符串比较大小

字符串比较的规则 比较规则: 从字符串的第1个字符开始比较大小,比较它们的ASCII码值。

  1. 大写字母小于小写字母,如:A 65 a 97

  2. 所有的数字都小于字母 说出如下字符串的比较结果:

字符串1 结果 字符串2 "abcde" < "ac" "abc" > "ab" "ab" > "AB" "1000" < "2" 注:String、Integer、Double、Character、Boolean、Float类都已经实现了Comparable接口

public class Demo04String {

    public static void main(String[] args) {
        String s1 = "1000";
        String s2 = "2";
        String s3 = "4";
        String s4 = "4";
        System.out.println( s1.compareTo(s2));  //-1
        System.out.println( s3.compareTo(s2));  //2
        System.out.println( s3.compareTo(s4));  //0
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 字符串排序案例

案例需求: 有一个字符串数组,包含5个字符串,请按从小到大的顺序输出字符串。

字符串数组如下:"Jack","Rose","make","tom","1000","9"

# 案例代码:
public class Demo05StringSort {
    public static void main(String[] args) {
        String[] names = {"Jack","Rose","make","tom","1000","9"};
        System.out.println("排序前:");
        System.out.println(Arrays.toString(names));
        Arrays.sort(names);
        System.out.println("排序后:");
        System.out.println(Arrays.toString(names));
        //创建自定义的比较器
        Arrays.sort(names, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o2.compareTo(o1);
            }
         });
        System.out.println("排序后:");
        System.out.println(Arrays.toString(names));
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# System类

System类代表系统,系统级的很多属性和控制方法都放置在该类的内部。 该类位于java.lang包。

由于该类的构造器是private的,所以无法创建该类的对象,也就是无法实 例化该类。其内部的成员变量和成员方法都是static的,所以也可以很方便 的进行调用。

# 成员变量

System类内部包含in、out和err三个成员变量,分别代表标准输入流 (键盘输入),标准输出流(显示器)和标准错误输出流(显示器)。

# 成员方法

  • native long currentTimeMillis(): 该方法的作用是返回当前的计算机时间,时间的表达格式为当前计算机时 间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数。

  • void exit(int status): 该方法的作用是退出程序。其中status的值为0代表正常退出,非零代表 异常退出。使用该方法可以在图形界面编程中实现程序的退出功能等。

  • void gc(): 该方法的作用是请求系统进行垃圾回收。至于系统是否立刻回收,则 取决于系统中垃圾回收算法的实现以及系统执行时的情况。

  • String getProperty(String key): 该方法的作用是获得系统中属性名为key的属性对应的值。

# Math类

Math类 java.lang.Math提供了一系列静态方法用于科学计算。其方法的参数和返回 值类型一般为double型。

image-20200615163223060

# BigInteger与BigDecimal

  • Integer类作为int的包装类,能存储的最大整型值为231-1,Long类也是有限的, 最大为263-1。如果要表示再大的整数,不管是基本数据类型还是他们的包装类 都无能为力,更不用说进行运算了。
  • java.math包的BigInteger可以表示不可变的任意精度的整数。BigInteger 提供 所有 Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。 另外,BigInteger 还提供以下运算:模算术、GCD 计算、质数测试、素数生成、 位操作以及一些其他操作。
  • 构造器  BigInteger(String val):根据字符串构建BigInt eger对象

# BigInteger类

# 常用方法

image-20200615163036656

# BigDecimal类

  • 一般的Float类和Double类可以用来做科学计算或工程计算,但在商业计算中, 要求数字精度比较高,故用到java.math.BigDecimal类。 BigDecimal类支持不可变的、任意精度的有符号十进制定点数。
  • 构造器
    • public BigDecimal(double val)
    • public BigDecimal(String val)
  • 常用方法
    • public BigDecimal add(BigDecimal augend)
    • public BigDecimal subtract(BigDecimal subtrahend)
    • public BigDecimal multiply(BigDecimal multiplicand)
    • public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)