常量定义:final
public class HelloWorld {
// 静态常量
public static final double PI = 3.14;
// 声明成员常量
final int y = 10;
public static void main(String[] args) {
// 声明局部常量
final double x = 3.3;
}
}
变量声明、赋值
String username,address,phone,tel; // 声明多个变量
int num1=12,num2=23,result=35; // 声明并初始化多个变量
成员变量
名称 | 修饰 | 访问 | 生命周期 |
---|---|---|---|
全局变量(实例变量) | 无 static 修饰 | 对象名.变量名 | 只要对象被当作引用,实例变量就将存在 |
静态变量(类变量) | 用 static 修饰 | 类名.变量名或对象名.变量名 | 其生命周期取决于类的生命周期。类被垃圾回收机制彻底回收时才会被销毁 |
数据类型
一个值要能被真正看作 float,它必须以 f(或 F)后缓结束;否则,会被当作 double 值。对 double 值来说,d(或 D)后缓是可选的。
一个 double 类型的数据与一个 int 类型的数据相乘后得到的结果类型为 double,
在 Java 语言中,布尔类型的值不能转换成任何数据类型,true 常量不等于 1,而 false 常量也不等于 0。这两个值只能赋给声明为 boolean 类型的变量,或者用于布尔运算表达式中。
自动类型转换
- 数值型数据的转换:byte→short→int→long→float→double。
- 字符型转换为整型:char→int。
当两种数据类型不兼容,或目标类型的取值范围小于源类型时,自动转换将无法进行,这时就需要进行强制类型转换。
int a = 3;
double b = 5.0;
a = (int)b;
不转会报错:Type mismatch: cannot convert from int to byte
运算符
短路与(&&)和短路或(||)能够采用最优化的计算方式,从而提高效率。在实际编程时,应该优先考虑使用短路与和短路或。
流程控制语句
字符串处理
类型转换
String 字符串转整型 int 有以下两种方式:
- Integer.parseInt(str)
- Integer.valueOf(str).intValue()
int转换为String
整型 int 转 String 字符串类型有以下 3 种方法:
- String s = String.valueOf(i);
- String s = Integer.toString(i);
- String s = “” + i;
还有valueOf() 、parse()和toString()、注意 valueOf 括号中的值不能为空,否则会报空指针异常(NullPointerException)。
常见处理方法
字符串拼接concat()、“+”;字符串长度:length();大小写转换:toLowerCase()、roUpperCase()
去除空格:trim()、提取:subString()、分割:split()、替换:replace、等等
StringBuffer
string的方法都会返回一个新的String对象,为应对频繁对字符串做修改操作的场景,StringBuffer是可修改的;而StringBuilder线程不安全很少使用
主要方法:https://www.runoob.com/java/java-stringbuffer.html
数字日期处理
Math:api:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Math.html
随机数:random
格式化:DecimalFormat
大数:BigInteger、BigDecimal(小数)
Date类:
new Date()
代码中格式化:
importjava.text.SimpleDateFormat;
SimpleDateFormatformat=newSimpleDateFormat(“yyyy-MM-dd”);
Calender类:
不能new、因为 Calendar 类是一个抽象类,
Calendar c = Calender.getInstance();
api:https://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html
DateFormat类、SimpleDateFormat类:用来格式化日期
内置包装类
在Java的设计中提倡一种思想,即一切皆对象。但是从数据类型的划分中,我们知道 Java 中的数据类型分为基本数据类型和引用数据类型,但是基本数据类型怎么能够称为对象呢?于是 Java 为每种基本数据类型分别设计了对应的类,称之为包装类(Wrapper Classes),也有地方称为外覆类或数据类型类。
基本数据类型转换为包装类的过程称为装箱,例如把 int 包装成 Integer 类的对象;包装类变为基本数据类型的过程称为拆箱,例如把 Integer 类的对象重新简化为 int。
Integerobj =newInteger(m); // 手动装箱
Integerobj = m; // 自动装箱
Object类
toString()、equals() 方法和 getClass() 方法在 Java 程序中比较常用
getClass() 方法返回对象所属的类,是一个 Class 对象。通过 Class 对象可以获取该类的各种信息,包括类名、父类以及它所实现接口的名字等。
public class Test02 {
public static void printClassInfo(Object obj) {
// 获取类名
System.out.println("类名:" + obj.getClass().getName());
// 获取父类名
System.out.println("父类:" + obj.getClass().getSuperclass().getName());
System.out.println("实现的接口有:");
// 获取实现的接口并输出
for (int i = 0; i < obj.getClass().getInterfaces().length; i++) {
System.out.println(obj.getClass().getInterfaces()[i]);
}
}
public static void main(String[] args) {
String strObj = new String();
printClassInfo(strObj);
}
}
Integer类
String str = "456";
int num = Integer.parseInt(str); // 将字符串转换为int类型的数值
int i = 789;
String s = Integer.toString(i); // 将int类型的数值转换为字符串
int max_value = Integer.MAX_VALUE; // 获取 int 类型可取的最大值
int min_value = Integer.MIN_VALUE; // 获取 int 类型可取的最小值
int size = Integer.SIZE; // 获取 int 类型的二进制位
Class c = Integer.TYPE; // 获取基本类型 int 的 Class 实例
Float类
String str = "456.7";
float num = Float.parseFloat(str); // 将字符串转换为 float 类型的数值
float f = 123.4f;
String s = Float.toString(f); // 将 float 类型的数值转换为字符串
float max_value = Float.MAX_VALUE; // 获取 float 类型可取的最大值
float min_value = Float.MIN_VALUE; // 获取 float 类型可取的最小值
float min_normal = Float.MIN_NORMAL; // 获取 float 类型可取的最小标准值
float size = Float.SIZE; // 获取 float 类型的二进制位
Double、number类
基本类似
Character类
Boolean、Byte、System类
类似
System 类有 3 个静态成员变量,分别是 PrintStream out、InputStream in 和 PrintStream err。
System 类中提供了一些系统级的操作方法,常用的方法有 arraycopy()、currentTimeMillis()、exit()、gc() 和 getProperty()。
arraycopy:该方法的作用是数组复制,即从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束
currentTimeMillis:该方法的作用是返回当前的计算机时间
exit:该方法的作用是终止当前正在运行的 Java 虚拟机
gc:该方法的作用是请求系统进行垃圾回收,完成内存中的垃圾清除
getProperty:该方法的作用是获得系统中属性名为 key 的属性对应的值
数组处理
一维数组
// 一维数组创建
type[] arrayName; // 数据类型[] 数组名;
// 分配空间
arrayName = new type[size]; // 数组名 = new 数据类型[数组长度];
// 指定值
type[] arrayName = new type[]{值 1,值 2,值 3,值 4,• • •,值 n};
二维数组
// 创建
type arrayName[][]; // 数据类型 数组名[][];
//初始化
type[][] arrayName = new type[][]{值 1,值 2,值 3,…,值 n}; // 在定义时初始化
type[][] arrayName = new type[size1][size2]; // 给定空间,在赋值
type[][] arrayName = new type[size][]; // 数组第二维长度为空,可变化
数组常用方法
equals()、fill()、binarySearch()查找指定元素在进行数组查询之前,必须对数组进行排序(可以使用 sort() 方法)、
复制:
copyOf() 方法是复制数组至指定长度,copyOfRange() 方法则将指定数组的指定长度复制到一个新数组中。
arraycopy:目标数组必须已经存在,且不会被重构,相当于替换目标数组中的部分元素
System.arraycopy(dataType[] srcArray,int srcIndex,int destArray,int destIndex,int length)
clone() 可以创建一个有单独内存空间的对象、返回值是 Object 类型,要使用强制类型转换为适当的类型
int[] targetArray=(int[])sourceArray.clone();
排序:
升序:Array.sort(arr)
降序:Array.sort(arr, Collections.reverseOrder())
类和对象
类的定义:
[public][abstract|final]class<class_name>[extends<class_name>][implements<interface_name>] {
// 定义属性部分
<property_type><property1>;
<property_type><property2>;
<property_type><property3>;
…
// 定义方法部分
function1();
function2();
function3();
…
}
上述语法中各关键字的描述如下。
public
:表示“共有”的意思。如果使用 public 修饰,则可以被其他类和程序访问。每个 Java 程序的主类都必须是 public 类,作为公共工具供其他类和程序使用的类应定义为 public 类。abstract
:如果类被 abstract 修饰,则该类为抽象类,抽象类不能被实例化,但抽象类中可以有抽象方法(使用 abstract 修饰的方法)和具体方法(没有使用 abstract 修饰的方法)。继承该抽象类的所有子类都必须实现该抽象类中的所有抽象方法(除非子类也是抽象类)。final
:如果类被 final 修饰,则不允许被继承。class
:声明类的关键字。class_name
:类的名称。extends
:表示继承其他类。implements
:表示实现某些接口。property_type
:表示成员变量的类型。property
:表示成员变量名称。function()
:表示成员方法名称
类的属性
[public|protected|private][static][final]<type><variable_name>
- public、protected、private:用于表示成员变量的访问权限。
- static:表示该成员变量为类变量,也称为静态变量。
- final:表示将该成员变量声明为常量,其值无法更改。
- type:表示变量的类型。
- variable_name:表示变量名称。
this 关键字是Java常用的关键字,可用于任何实例方法内指向当前对象,也可指向对其调用当前方法的对象,或者在需要当前类型对象引用时使用。
垃圾回收机制,简称 GC
在 Java 的 Object 类中还提供了一个 protected 类型的 finalize() 方法,因此任何 Java 类都可以覆盖这个方法,在这个方法中进行释放对象所占有的相关资源的操作。 在 Java 虚拟机的堆区,每个对象都可能处于以下三种状态之一。 1)可触及状态:当一个对象被创建后,只要程序中还有引用变量引用它,那么它就始终处于可触及状态。 2)可复活状态:当程序不再有任何引用变量引用该对象时,该对象就进入可复活状态。在这个状态下,垃圾回收器会准备释放它所占用的内存,在释放之前,会调用它及其他处于可复活状态的对象的 finalize() 方法,这些 finalize() 方法有可能使该对象重新转到可触及状态。 3)不可触及状态:当 Java 虚拟机执行完所有可复活对象的 finalize() 方法后,如果这些方法都没有使该对象转到可触及状态,垃圾回收器才会真正回收它占用的内存。
Java访问控制修饰符
1. private
用 private 修饰的类成员,只能被该类自身的方法访问和修改,而不能被任何其他类(包括该类的子类)访问和引用。因此,private 修饰符具有最高的保护级别。例如,设 PhoneCard 是电话卡类,电话卡都有密码,因此该类有一个密码域,可以把该类的密码域声明为私有成员。
2. friendly(默认)
如果一个类没有访问控制符,说明它具有默认的访问控制特性。这种默认的访问控制权规定,该类只能被同一个包中的类访问和引用,而不能被其他包中的类使用,即使其他包中有该类的子类。这种访问特性又称为包访问性(package private)。同样,类内的成员如果没有访问控制符,也说明它们具有包访问性,或称为友元(friend)。定义在同一个文件夹中的所有类属于一个包,所以前面的程序要把用户自定义的类放在同一个文件夹中(Java 项目默认的包),以便不加修饰符也能运行。
3. protected
用保护访问控制符 protected 修饰的类成员可以被三种类所访问:该类自身、与它在同一个包中的其他类以及在其他包中的该类的子类。使用 protected 修饰符的主要作用,是允许其他包中它的子类来访问父类的特定属性和方法,否则可以使用默认访问控制符。
4. public
当一个类被声明为 public 时,它就具有了被其他包中的类访问的可能性,只要包中的其他类在程序中使用 import 语句引入 public 类,就可以访问和引用这个类。 类中被设定为 public 的方法是这个类对外的接口部分,避免了程序的其他部分直接去操作类内的数据,实际就是数据封装思想的体现。每个 Java 程序的主类都必须是 public 类,也是基于相同的原因。
static关键字
在类中,使用 static 修饰符修饰的属性(成员变量)称为静态变量,也可以称为类变量,常量称为静态常量,方法称为静态方法或类方法,它们统称为静态成员,归整个类所有。 静态成员不依赖于类的特定实例,被类的所有实例共享,就是说 static 修饰的方法或者变量不需要依赖于对象来进行访问,只要这个类被加载,Java虚拟机就可以根据类名找到它们。
静态变量与实例变量的区别如下:
1)静态变量
- 运行时,Java 虚拟机只为静态变量分配一次内存,在加载类的过程中完成静态变量的内存分配。
- 在类的内部,可以在任何方法内直接访问静态变量。
- 在其他类中,可以通过类名访问该类中的静态变量。
2)实例变量
- 每创建一个实例,Java 虚拟机就会为实例变量分配一次内存。
- 在类的内部,可以在非静态方法中直接访问实例变量。
- 在本类的静态方法或其他类中则需要通过类的实例对象进行访问。
使用 import 可以省略写包名,而使用 import static 可以省略类名。
import static package.ClassName.*;
可变参数
声明方式
methodName({paramList},paramType…paramName)
注意:可变参数必须定义在参数列表的最后。
public class StudentTestMethod {
// 定义输出考试学生的人数及姓名的方法
public void print(String...names) {
int count = names.length; // 获取总个数
System.out.println("本次参加考试的有"+count+"人,名单如下:");
for(int i = 0;i < names.length;i++) {
System.out.println(names[i]);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
StudentTestMethod student = new StudentTestMethod();
student.print("张强","李成","王勇"); // 传入3个值
student.print("马丽","陈玲");
}
}
print 方法的参数为可变参数
构造方法
构造方法是类的一种特殊方法,用来初始化类的一个新的对象,在创建对象(new 运算符)之后自动调用。Java中的每个类都有一个默认的构造方法,并且可以有一个以上的构造方法。Java 构造方法有以下特点:
- 方法名必须与类名相同
- 可以有 0 个、1 个或多个参数
- 没有任何返回值,包括 void
- 默认返回类型就是对象类型本身
- 只能与 new 运算符结合使用
构造方法不能被 static、final、synchronized、abstract 和 native(类似于 abstract)修饰。
在一个类中,与类名相同的方法就是构造方法。每个类可以具有多个构造方法,但要求它们各自包含不同的方法参数。
在一个类中定义多个具有不同参数的同名方法,这就是方法的重载。
如果在类中没有定义任何一个构造方法,则 Java 会自动为该类生成一个默认的构造方法。默认的构造方法不包含任何参数,并且方法体为空。如果类中显式地定义了一个或多个构造方法,则 Java 不再提供默认构造方法。
析构方法
析构方法与构造方法相反,当对象脱离其作用域时(例如对象所在的方法已调用完毕),系统自动执行析构方法。析构方法往往用来做清理垃圾碎片的工作,例如在建立对象时用 new 开辟了一片内存空间,应退出前在析构方法中将其释放。
继承和多态
继承
修饰符 class class_name extends extend_class {
// 类的主体
}
Java 不支持多继承,只允许一个类直接继承另一个类,即子类只能有一个直接父类,extends 关键字后面只能有一个类名。
super关键字详解
public class Student extends Person {
public Student(String name, int age, String birth) {
super(name, age); // 调用父类中含有2个参数的构造方法
}
public Student(String name, int age, String sex, String birth) {
super(name, age, sex); // 调用父类中含有3个参数的构造方法
}
}
使用 super 访问父类中的成员与 this 关键字的使用相似,只不过它引用的是子类的父类
super和this的区别
this 指的是当前对象的引用,super 是当前对象的父对象的引用。
super 关键字的用法:
- super.父类属性名:调用父类中的属性
- super.父类方法名:调用父类中的方法
- super():调用父类的无参构造方法
- super(参数):调用父类的有参构造方法
如果构造方法的第一行代码不是 this() 和 super(),则系统会默认添加 super()。
this 关键字的用法:
- this.属性名:表示当前对象的属性
- this.方法名(参数):表示调用当前对象的方法
关于Javasuper 和 this 关键字的异同,可简单总结为以下几条。
- 子类和父类中变量或方法名称相同时,用 super 关键字来访问。可以理解为 super 是指向自己父类对象的一个指针。在子类中调用父类的构造方法。
- this 是自身的一个对象,代表对象本身,可以理解为 this 是指向对象本身的一个指针。在同一个类中调用其它方法。
- this 和 super 不能同时出现在一个构造方法里面,因为 this 必然会调用其它的构造方法,其它的构造方法中肯定会有 super 语句的存在,所以在同一个构造方法里面有相同的语句,就失去了语句的意义,编译器也不会通过。
- this( ) 和 super( ) 都指的是对象,所以,均不可以在 static 环境中使用,包括 static 变量、static 方法和 static 语句块。
从本质上讲,this 是一个指向对象本身的指针, 然而 super 是一个 Java 关键字。
TODO:Java对象类型转换:向上转型和向下转型
重载
Java允许同一个类中定义多个同名方法,只要它们的形参列表不同即可。如果同一个类中包含了两个或两个以上方法名相同的方法,但形参列表不同,这种情况被称为方法重载(overload)。
重写
在子类中如果创建了一个与父类中相同名称、相同返回值类型、相同参数列表的方法,只是方法体中的实现不同,以实现不同于父类的功能,这种方式被称为方法重写(override),又称为方法覆盖。当父类中的方法无法满足子类需求或子类具有特有功能的时候,需要方法重写。
在重写方法时,需要遵循下面的规则:
- 参数列表必须完全与被重写的方法参数列表相同。
- 返回的类型必须与被重写的方法的返回类型相同(Java1.5 版本之前返回值类型必须一样,之后的 Java 版本放宽了限制,返回值类型必须小于或者等于父类方法的返回值类型)。
- 访问权限不能比父类中被重写方法的访问权限更低(public>protected>default>private)。
- 重写方法一定不能抛出新的检査异常或者比被重写方法声明更加宽泛的检査型异常。例如,父类的一个方法声明了一个检査异常 IOException,在重写这个方法时就不能抛出 Exception,只能拋出 IOException 的子类异常,可以抛出非检査异常。
另外还要注意以下几条:
- 重写的方法可以使用 @Override 注解来标识。
- 父类的成员方法只能被它的子类重写。
- 声明为 final 的方法不能被重写。
- 声明为 static 的方法不能被重写,但是能够再次声明。
- 构造方法不能被重写。
- 子类和父类在同一个包中时,子类可以重写父类的所有方法,除了声明为 private 和 final 的方法。
- 子类和父类不在同一个包中时,子类只能重写父类的声明为 public 和 protected 的非 final 方法。
- 如果不能继承一个方法,则不能重写这个方法。
多态性
Java实现多态有 3 个必要条件:继承、重写和向上转型。只有满足这 3 个条件,开发人员才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而执行不同的行为。
- 继承:在多态中必须存在有继承关系的子类和父类。
- 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
- 向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才既能可以调用父类的方法,又能调用子类的方法。
instanceof关键字详解
在 Java 中可以使用 instanceof 关键字判断一个对象是否为一个类(或接口、抽象类、父类)的实例,
和js中类似
抽象(abstract)类
<abstract>class<class_name> {
<abstract><type><method_name>(parameter-iist);
}
抽象方法的 3 个特征如下:
- 抽象方法没有方法体
- 抽象方法必须存在于抽象类中
- 子类重写父类时,必须重写父类所有的抽象方法
接口(Interface)
一种特殊的类,不同的是接口的成员没有执行体,是由全局常量和公共的抽象方法所组成。
实现:
<public> class <class_name> [extends superclass_name] [implements interface1_name[, interface2_name…]] {
// 主体
}
内部类
内部类拥有外部类的所有元素的访问权限。内部类可以分为:实例内部类、静态内部类和成员内部类,
- 外部类只有两种访问级别:public 和默认;内部类则有 4 种访问级别:public、protected、 private 和默认。
TODO 实例内部类
TODO
异常处理
try {
// 可能会发生异常的语句
} catch(ExceptionType e) {
// 处理异常语句
} finally {
// 清理代码块
}
try-catch-finally
使用 try-catch-finally 语句时需注意以下几点:
- 异常处理语法结构中只有 try 块是必需的,也就是说,如果没有 try 块,则不能有后面的 catch 块和 finally 块;
- catch 块和 finally 块都是可选的,但 catch 块和 finally 块至少出现其中之一,也可以同时出现;
- 可以有多个 catch 块,捕获父类异常的 catch 块必须位于捕获子类异常的后面;
- 不能只有 try 块,既没有 catch 块,也没有 finally 块;
- 多个 catch 块必须位于 try 块之后,finally 块必须位于所有的 catch 块之后。
- finally 与 try 语句块匹配的语法格式,此种情况会导致异常丢失,所以不常见。
通常情况下不在 finally 代码块中使用 return 或 throw 等导致方法终止的语句,否则将会导致 try 和 catch 代码块中的 return 和 throw 语句失效
trows 声明异常
returnType method_name(paramList) throws Exception 1,Exception2,…{…}
使用 throws 声明抛出异常的思路是,当前方法不知道如何处理这种类型的异常,该异常应该由向上一级的调用者处理;如果 main 方法也不知道如何处理这种类型的异常,也可以使用 throws 声明抛出异常,该异常将交给 JVM 处理。JVM 对异常的处理方法是,打印异常的跟踪栈信息,并中止程序运行,这就是前面程序在遇到异常后自动结束的原因。
子类方法声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或相同,子类方法声明抛出的异常不允许比父类方法声明抛出的异常多
throw 拋出异常
throw 语句用来直接拋出一个异常,后接一个可拋出的异常类对象
throw ExceptionObject;
//eg
throw new IllegalArgumentException("用户名只能由字母和数字组成!");
throws 关键字和 throw 关键字在使用上的几点区别如下:
- throws 用来声明一个方法可能抛出的所有异常信息,表示出现异常的一种可能性,但并不一定会发生这些异常;throw 则是指拋出的一个具体的异常类型,执行 throw 则一定抛出了某种异常对象。
- 通常在一个方法(类)的声明处通过 throws 声明方法(类)可能拋出的异常信息,而在方法(类)内部通过 throw 声明一个具体的异常信息。
- throws 通常不用显示地捕获异常,可由系统自动将所有捕获的异常信息抛给上级方法; throw 则需要用户自己捕获相关的异常,而后再对其进行相关包装,最后将包装后的异常信息抛出。
Java.util.logging
logger.warning(message);
Java集合、泛型和枚举
集合
Collection接口详解
Collection 接口是 List、Set 和 Queue 接口的父接口,通常情况下不被直接使用。
List:ArrayList、LinkedList
List 是一个有序、可重复的集合,集合中每个元素都有其对应的顺序索引。
- ArrayList 类实现了可变数组的大小,存储在内的数据称为元素。它还提供了快速基于索引访问元素的方式,对尾部成员的增加和删除支持较好
- LinkedList 类采用链表结构保存对象,这种结构的优点是便于向集合中插入或者删除元素。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高,但是 LinkedList 类随机访问元素的速度则相对较慢。这里的随机访问是指检索集合中特定索引位置的元素。
ArrayList 是基于动态数组数据结构的实现,访问元素速度优于 LinkedList。LinkedList 是基于链表数据结构的实现,占用的内存空间比较大,但在批量插入或删除数据时优于 ArrayList。 对于快速访问对象的需求,使用 ArrayList 实现执行效率上会比较好。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高。
Set: HashSet、TreeSet
Set 集合中的对象不按特定的方式排序,Set 集合中不能包含重复的对象,并且最多只允许包含一个 null 元素。
- HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时就是使用这个实现类。HashSet 是按照 Hash 算法来存储集合中的元素。因此具有很好的存取和查找性能。
- 不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。
- HashSet 不是同步的,如果多个线程同时访问或修改一个 HashSet,则必须通过代码来保证其同步。
- 集合元素值可以是 null。
- TreeSet 类同时实现了 Set 接口和 SortedSet 接口。SortedSet 接口是 Set 接口的子接口,可以实现对集合进行自然排序(升序);TreeSet 只能对实现了 Comparable 接口的类对象进行排序;在使用自然排序时只能向 TreeSet 集合中添加相同数据类型的对象,否则会抛出 ClassCastException 异常
Map集合详解
Map 是一种键-值对(key-value)集合,用于保存具有映射关系的数据。 key 和 value 都可以是任何引用类型的数据。Map 的 key 不允许重复,value 可以重复 Map 中的 key 和 value 之间存在单向一对一关系,即通过指定的 key,总能找到唯一的、确定的 value。 Map 接口主要有两个实现类:HashMap 类和 TreeMap 类。其中,HashMap 类按哈希算法来存取键对象,而 TreeMap 类可以对键对象进行排序。
遍历Map集合的四种方式
- 在 for 循环中使用 entries 实现 Map 的遍历
public static void main(String[] args) {
Map<String, String> myMap = new HashMap<String, String>();//声明
map.put("Java入门教程", "http://c.biancheng.net/java/");
map.put("C语言入门教程", "http://c.biancheng.net/c/");
for (Map.Entry<String, String> entry : myMap.entrySet()) { // 这行是固定写法
String mapKey = entry.getKey();
String mapValue = entry.getValue();
System.out.println(mapKey + ":" + mapValue);
}
}
- 使用 for-each 循环遍历 key 或者 values,一般适用于只需要 Map 中的 key 或者 value 时使用。性能上比 entrySet 较好。
Map<String, String> map = new HashMap<String, String>();
map.put("Java入门教程", "http://c.biancheng.net/java/");
map.put("C语言入门教程", "http://c.biancheng.net/c/");
// 打印键集合
for (String key : map.keySet()) {
System.out.println(key);
}
// 打印值集合
for (String value : map.values()) {
System.out.println(value);
}
- 使用迭代器(Iterator)遍历
Map<String, String> map = new HashMap<String, String>();
map.put("Java入门教程", "http://c.biancheng.net/java/");
map.put("C语言入门教程", "http://c.biancheng.net/c/");
Iterator<Entry<String, String>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Entry<String, String> entry = entries.next();
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + ":" + value);
}
- 通过键找值遍历,这种方式的效率比较低,因为本身从键取值是耗时的操作。
for(String key : map.keySet()){
String value = map.get(key);
System.out.println(key+":"+value);
}
Collections类操作集合详解
Collections 提供了如下方法用于对 List 集合元素进行排序。
- void reverse(List list):对指定 List 集合元素进行逆向排序。
- void shuffle(List list):对 List 集合元素进行随机排序(shuffle 方法模拟了“洗牌”动作)。
- void sort(List list):根据元素的自然顺序对指定 List 集合的元素按升序进行排序。
- void sort(List list, Comparator c):根据指定 Comparator 产生的顺序对 List 集合元素进行排序。
- void swap(List list, int i, int j):将指定 List 集合中的 i 处元素和 j 处元素进行交换。
- void rotate(List list, int distance):当 distance 为正数时,将 list 集合的后 distance 个元素“整体”移到前面;当 distance 为负数时,将 list 集合的前 distance 个元素“整体”移到后面。该方法不会改变集合的长度。
// 使用方法:
import java.util.Collections;
Collections.sort(prices); // 调用sort()方法对集合进行排序
Collections 还提供了如下常用的用于查找、替换集合元素的方法。
- int binarySearch(List list, Object key):使用二分搜索法搜索指定的 List 集合,以获得指定对象在 List 集合中的索引。如果要使该方法可以正常工作,则必须保证 List 中的元素已经处于有序状态。
- Object max(Collection coll):根据元素的自然顺序,返回给定集合中的最大元素。
- Object max(Collection coll, Comparator comp):根据 Comparator 指定的顺序,返回给定集合中的最大元素。
- Object min(Collection coll):根据元素的自然顺序,返回给定集合中的最小元素。
- Object min(Collection coll, Comparator comp):根据 Comparator 指定的顺序,返回给定集合中的最小元素。
- void fill(List list, Object obj):使用指定元素 obj 替换指定 List 集合中的所有元素。
- int frequency(Collection c, Object o):返回指定集合中指定元素的出现次数。
- int indexOfSubList(List source, List target):返回子 List 对象在父 List 对象中第一次出现的位置索引;如果父 List 中没有出现这样的子 List,则返回 -1。
- int lastIndexOfSubList(List source, List target):返回子 List 对象在父 List 对象中最后一次出现的位置索引;如果父 List 中没有岀现这样的子 List,则返回 -1。
- boolean replaceAll(List list, Object oldVal, Object newVal):使用一个新值 newVal 替换 List 对象的所有旧值 oldVal。
Collections 类的 copy() 静态方法用于将指定集合中的所有元素复制到另一个集合中。
void copy(List <? super T> dest,List<? extends T> src)
Iterator(迭代器)遍历Collection集合元素
Iterator 接口隐藏了各种 Collection 实现类的底层细节,向应用程序提供了遍历 Collection 集合元素的统一编程接口。Iterator 接口里定义了如下 4 个方法。
- boolean hasNext():如果被迭代的集合元素还没有被遍历完,则返回 true。
- Object next():返回集合里的下一个元素。
- void remove():删除集合里上一次 next 方法返回的元素。
- void forEachRemaining(Consumer action):这是 Java 8 为 Iterator 新增的默认方法,该方法可使用 Lambda 表达式来遍历集合元素
当使用 Iterator 迭代访问 Collection 集合元素时**,Collection 集合里的元素不能被改变**,只有通过 Iterator 的 remove() 方法删除上一次 next() 方法返回的集合元素才可以,否则将会引发“java.util.ConcurrentModificationException”异常。
Stream操作Collection集合
Stream 提供了大量的方法进行聚集操作,这些方法既可以是“中间的”(intermediate),也可以是 “末端的”(terminal)。
- 中间方法:中间操作允许流保持打开状态,并允许直接调用后续方法。上面程序中的 map() 方法就是中间方法。中间方法的返回值是另外一个流。
- 末端方法:末端方法是对流的最终操作。当对某个 Stream 执行末端方法后,该流将会被“消耗”且不再可用。上面程序中的 sum()、count()、average() 等方法都是末端方法。
可通过流式 API 来操作:
objs.stream().filter(ele -> ((String) ele).contains(“C语言中文网”)).count() 这种就是流式,相当于js的链式
public class CollectionStream {
public static void main(String[] args) {
// 创建一个集合
Collection objs = new HashSet();
objs.add(new String("C语言中文网Java教程"));
objs.add(new String("C语言中文网C++教程"));
objs.add(new String("C语言中文网C语言教程"));
objs.add(new String("C语言中文网Python教程"));
objs.add(new String("C语言中文网Go教程"));
// 统计集合中出现“C语言中文网”字符串的数量
System.out.println(objs.stream().filter(ele -> ((String) ele).contains("C语言中文网")).count()); // 输出 5
// 统计集合中出现“Java”字符串的数量
System.out.println(objs.stream().filter(ele -> ((String) ele).contains("Java")).count()); // 输出 1
// 统计集合中出现字符串长度大于 12 的数量
System.out.println(objs.stream().filter(ele -> ((String) ele).length() > 12).count()); // 输出 1
// 先调用Collection对象的stream ()方法将集合转换为Stream
// 再调用Stream的mapToInt()方法获取原有的Stream对应的IntStream
objs.stream().mapToInt(ele -> ((String) ele).length())
// 调用forEach()方法遍历IntStream中每个元素
.forEach(System.out::println);// 输出 11 11 12 10 14
}
}
不可变集合
Set、List、Map 的 of() 方法即可创建包含 N 个元素的不可变集合,
public class Java9Collection {
public static void main(String[] args) {
// 创建包含4个元素的Set集合
Set set = Set.of("Java", "Kotlin", "Go", "Swift");
System.out.println(set);
// 不可变集合,下面代码导致运行时错误
// set.add("Ruby");
// 创建包含4个元素的List集合
List list = List.of(34, -25, 67, 231);
System.out.println(list);
// 不可变集合,下面代码导致运行时错误
// list.remove(1);
// 创建包含3个key-value对的Map集合
Map map = Map.of("语文", 89, "数学", 82, "英语", 92);
System.out.println(map);
// 不可变集合,下面代码导致运行时错误
// map.remove("语文");
// 使用Map.entry()方法显式构建key-value对
Map map2 = Map.ofEntries(Map.entry("语文", 89), Map.entry("数学", 82), Map.entry("英语", 92));
System.out.println(map2);
}
}
泛型
泛型本质上是提供类型的“类型参数”,也就是参数化类型
如果 static 方法需要使用泛型能力,就必须使其成为泛型方法。
泛型方法定义
[访问权限修饰符] [static] [final] <类型参数列表> 返回值类型 方法名([形式参数列表])
//eg
public static <T> List find(Class<T> cs,int userId){}
限制泛型可用类型
在 Java 中默认可以使用任何类型来实例化一个泛型类对象,也可以对泛型类实例的类型进行限制
class 类名称<T extends anyClass>
// 限制ListClass的泛型类型必须实现List接口
public class ListClass<T extends List> {
public static void main(String[] args) {
// 实例化使用ArrayList的泛型类ListClass,正确
ListClass<ArrayList> lc1 = new ListClass<ArrayList>();
// 实例化使用LinkedList的泛型类LlstClass,正确
ListClass<LinkedList> lc2 = new ListClass<LinkedList>();
// 实例化使用HashMap的泛型类ListClass,错误,因为HasMap没有实现List接口
// ListClass<HashMap> lc3=new ListClass<HashMap>();
}
}
ArrayList、LinkedList 都实现了List接口,所以正确,HashMap 没有实现 List 接口,所以在实例化 ListClass 类时会报错。
使用类型通配符
泛型类名称<? extends List>a = null;
A<? extends List>a = null;
a = new A<ArrayList> (); // 正确
b = new A<LinkedList> (); // 正确
c = new A<HashMap> (); // 错误
继承泛型类和实现泛型接口
public class FatherClass<T1>{}
public class SonClass<T1,T2,T3> extents FatherClass<T1>{}
枚举
enum-modifiers enum enumname:enum-base {
enum-body,
}
任意两个枚举成员不能具有相同的名称,且它的常数值必须在该枚举的基础类型的范围之内,多个枚举成员之间使用逗号分隔。之后便可以通过枚举类型名直接引用常量,
枚举类
Java 中的每一个枚举都继承自 java.lang.Enum 类。当使用枚举类型成员时,直接使用枚举名称调用成员即可。
s.compareTo(Sex.values()[i])
Signal.values()[i]
Signal.values()[i].ordinal()//索引位置
Java 中的 enum 还可以跟 Class 类一样覆盖基类的方法。