for/for-each
1、for-each从JDK5.0开始引入
2、for-earch语法更简洁
3、for-earch避免越界错误
4、for可以删除元素,for-each不可以删除/替换元素
5、for-each遍历的时候,是不知道当前元素的具体位置索引
6、for-each只能正向遍历,不能反向遍历
7、for-each不能同时遍历2个集合
枚举类型
- 枚举类型:变量的取值只在一个有限集合内,如性别、星期几等
- Java5推出enum类型
- enum关键字声明枚举类型,且都是Enum的子类(但不需要写extends)
- enum内部有多少个值,就有多少个实例对象
- 不能new枚举类对象
/**
* @ClassName: Main01
* @Author: Tang
* @Date: 2021/6/2 20:21
*/
public class Main01 {
public static void main(String[] args) {
SizeEnum size1 = SizeEnum.SMALL;
SizeEnum size2 = SizeEnum.MEDIUM;
SizeEnum size3 = SizeEnum.EXTRA_lARGE;
SizeEnum size4 = SizeEnum.LARGE;
System.out.println(size1 + " " + size2 + " " + size3 + " " + size4);
}
}
enum SizeEnum {
SMALL,MEDIUM,LARGE,EXTRA_lARGE;
}
输出结果为
SMALL MEDIUM EXTRA_lARGE LARGE
- Java中的enum类型
- 除了枚举的内容,还可以添加属性/构造函数/方法
- 构造函数只能是package-private(default)或者private,内部调用
- 所有的枚举类型都是Enum的子类,也继承了对应的方法
- ordinal()返回枚举值所在的索引位置,从0开始
- compareTo()比较两个枚举值的索引位置大小
- toString()返回枚举值的字符串表示
- valueOf()将字符串初始化为枚举对象
- values()返回所有的枚举值
同一个类 | 同一个包 | 不同子包 | 不同包的非子类 | |
---|---|---|---|---|
private | ✓ | |||
default | ✓ | ✓ | ||
protected | ✓ | ✓ | ✓ | |
public | ✓ | ✓ | ✓ | ✓ |
public class Main01 {
public static void main(String[] args) {
SizeEnum size1 = SizeEnum.APPLE;
SizeEnum size2 = SizeEnum.ORANGE;
System.out.println(size1.getPrice() + " " + size2.getPrice() + " ");
}
}
enum SizeEnum {
APPLE(10),ORANGE(8);
private int price;
SizeEnum(int price) {
this.price = price;
}
public int getPrice() {
return this.price;
}
}
运行结果:
10 8
不定项参数
- 普通函数的形参列表是固定个数/类型/顺序
- JDK5提供了不定项参数(可变参数)功能
- 类型后加3个点,如int…/double…/String…/
- 可变参数,本质是一个数组
- 一个方法只能有一个不定项参数,且必须位于参数列表的最后
- 重载的优先级规则1:固定参数的方法,比可变参数优先级更高
- 重载的优先级规则2:调用语句,同时与两个带可变参数的方法匹配则会报错
public class Main02 {
public static void main(String[] args) {
print();
print("aaa", "bbb");
print("aaa", "bbb", "ccc");
}
public static void print(String... args) {
System.out.println(args.length);
for (String arg : args) {
System.out.println(arg);
}
}
}
运行结果:
0
2
aaa
bbb
3
aaa
bbb
ccc
静态导入
- import导入程序所需要的类
- import static导入一个类的静态方法和静态变量(JDK5引入)
- import static导入一个类的静态方法和静态变量
- 少使用*通配符,不滥用,最好具体到静态遍历或方法
- 静态方法名具有明确特征如parseDouble()和parseInt(),如有重名,需要补充类名
import static java.lang.Math.*;
import static java.lang.System.*;
import static java.lang.System.out;
/**
* @ClassName: Main
* @Author: Tang
* @Date: 2021/6/2 21:29
*/
public class Main03 {
public static void main(String[] args) {
int a = 3,b = 4,c = 0;
// 使用import static后可以省略Math
c = (int) sqrt(pow(a, 2) + pow(b, 2));
c = (int) Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
out.println("c is " + c);
System.out.println("c is " + c);
}
}
自动装箱和自动拆箱
- 从JDK5.0开始引入,简化基本数据类型和对象转换的写法
- 基本数据类型:boolean / byte / char / int / short / long / float / double
- 对象: Boolean / Byte / Character / Integer / Short / Long / Float / Double
public class Main04 {
public static void main(String[] args) {
// 自动装箱
Integer obj1 = 5;
// 没有自动装箱
Integer obj2 = Integer.valueOf(5);
// 自动拆箱
int a = obj1;
// 没有自动拆箱
int a2 = obj1.intValue();
}
}
装箱可以理解为将基本的数据类型包装成对象,拆箱则反之
自动装箱和拆箱注意事项
- 装箱和拆箱是编译器的工作,在class中已经添加转化。虚拟机没有自动装箱和拆箱的语句。
- ==:基本数据是否内容相同,对象是指针是否相同(内存同一个区域)
- 基本数据类型没有空值,对象有null
- 当一个基础数据类型与封装类进行==、+、-、*、/运算时,会将封装类进行拆箱,对基础数据类型进行运算。
- 谨慎使用多个非同类的数值类对象进行运算。
public static void main(String[] args) {
Integer a1 = 1000;
int a2 = 1000;
Integer a3 = 2000;
Long a4 = 2000L;
long a5 = 2000L;
// 拆箱再进行数值比较
System.out.println(a1 == a2); // true
System.out.println(a3 == (a1 + a2)); // true
System.out.println(a5 == (a1 + a2)); // true
// equals()要求同类,且内容相同
System.out.println(a3.equals(a1 + a2)); // true
System.out.println(a4.equals(a1 + a2)); // false
// a4的Long类型和(a1 + a2)的long类型等价,所以为true
System.out.println(a4.equals((long) (a1 + a2))); // true
// 不同类型不能比较
// System.out.println(a3 == a4);
}
Long.equals()源码:
public boolean equals(Object obj) {
if (obj instanceof Long) {
return value == ((Long)obj).longValue();
}
return false;
}
运行结果:
true
true
true
true
false
true
多异常并列
- 多个异常并列在以catch中
- 从JDK7.0开始引入,简化写法
try {
test();
} catch (IOException ex) {
// 异常处理
} catch (SQLException ex) {
// 异常处理
}
简化后
try {
test();
} catch (IOException | SQLException ex) {
// 异常处理
}
- 多个异常并列在一个catch中
- 多个异常之间不能有(直接 / 间接)继承关系,如果有,则报错
如下
try {
test();
} catch (IOException | FileNoFoundException ex) {
// IOException和FileNoFoundException之间有继承关系
// 所以报错了
}
数字中的下划线
- Java7的新语法:在数值字面量(literal)中使用下划线
- 增加数字的可读性和纠错性功能
- short / int / long / float / double均可用
- 下划线只能出现在数字之间,前后必须有数字
- 允许二 / 八 / 十 / 十六进制的数字中使用
public static void main(String[] args) {
// 二进制以0b开头
int a3 = 0b0111_1011_0001;
// 八进制以0开头
int a4 = 02_014;
// 可以多个下划线
int a5 = 1234__45;
// 十六进制
int a6 = 0x7_B_1;
float a7 = 3.56_78f;
double a8 = 1.3_45_67;
}
接口方法
- Java最初设计中,接口的方法都是没有实现的,公开的
- Java8推出接口的默认方法 / 静态方法(都带实现的),为了Lambda表达式提供支持
interface NewAnimal {
// 必须有default关键字来表示接口默认方法
public default void move() {
System.out.println("move ....");
}
}
- 规则1:默认方法不能重写Object中的方法
- 规则2:实现类可以继承 / 重写父接口的默认方法
- 规则3:接口可以继承 / 重写父接口的默认方法
- 规则4:当父类和父接口都有(同名同参数)默认方法,子类继承父类的默认方法,这样可以兼容JDK7及以前的代码
- 规则5:子类实现了2个接口(均有同名同参数的默认方法),那么编译失败在子类中重写这个default方法
基础接口
public interface Animal {
public void move();
}
带默认方法的接口
public interface NewAnimal {
public default void move() {
System.out.println("NewAnimal move....");
}
// 不能重写Object中的方法
/*public default String toString() {
return "NewAnimal";
}*/
}
抽象类实现基础接口
public abstract class NewFlyAnimal implements NewAnimal {
public void move() {
System.out.println("abstract class NewFlyAnimal move ...");
}
}
接口继承基础接口
public interface NewLandAnimal extends NewAnimal{
public default void move() {
System.out.println("NewLandAnimal move ...");
}
}
NewLandAnimal接口实现
public class NewLion implements NewLandAnimal {
public static void main(String[] args) {
new NewLion().move();
}
}
结果:
NewLandAnimal move …
继承NewFlyAnimal并同时实现NewLandAnimal
public class NewLion extends NewFlyAnimal implements NewLandAnimal {
public static void main(String[] args) {
// 当父类和父接口都有同名的方法,以父类的为主
// 这样就可以兼容JDK7以前的代码
new NewLion().move();
}
}
结果:
abstract class NewFlyAnimal move …
实现两个接口都有同名同参数的方法
public class NewLion implements NewLandAnimal, Animal{
public static void main(String[] args) {
new NewLion().move();
}
// 当实现的两个接口都含有同名方法,且至少有一个默认方法
// 则子类需要重写改方法避免歧义
public void move() {
// 必须指明指定接口的move方法
NewLandAnimal.super.move();
}
}
结果:
NewLandAnimal move …
接口的静态方法
- Java8接口的静态方法(带实现)
- 该静态方法属于本接口,不属于子类 / 子接口
- 子类(子接口)没有继承该静态方法,只能通过所在的接口名来调用
public class StaticSwan implements StaticAnimal {
public static void main(String[] args) {
StaticAnimal.move();
// 报错
// StaticSwan.move();
//报错
// new StaticSwan().move();
}
}
interface StaticAnimal {
public static void move() {
System.out.println("StaticAnimal move ...");
}
}
接口的私有方法
- 解决多个默认方法 / 静态方法的内容重复问题
- 私有方法属于本接口的,只能在本接口内使用,不属于子类 / 子接口
- 子类(子接口)没有继承该私有方法,也无法调用
- 静态私有方法可以被静态 / 默认方法调用,非静态私有方法被默认方法调用
接口与抽象类
相同点(截止至java12以前,接口和抽象类对比)
- 都是抽象的,都不能被实例化,即不能被new
- 都可以由实现方法
- 都可以不需要继承者实现所有的方法
不同点(截止至java12以前,接口和抽象类对比)
- 抽象类最多只能继承一个,接口可以实现多个
- 接口的变量默认是public static final,且必须有初值,子类不能修改;而抽象类的变量默认是default,子类可以继承修改
- 接口没有构造函数,抽象类有构造函数
- 接口没有main函数,抽象类可以有main函数
- 接口有public / default / private的方法,抽象类有public / private / protected不能写关键字的(default)的方法
try-with-resource语句
- 程序如果打开外部资源,那么在使用后需要正确关闭资源
- 考虑异常因素,Java提供try-catch-finally进行保证资源关闭
- JDK7提供try-with-resource,比try-catch-finally更简便
FileInputStream fis = ......;
try {
.......
} catch (Exception ex) {
.......
} finally {
if (null != fis) {
fis.close();
}
}
简化后:
// 使用完后会自动关闭小括号中的资源
try (FileInputStream fis = .......) {
......
} catch (Exception ex) {
.......
}
- JDK7提供的try-with-resource
- 资源要求定义在try中.若已经在外面定义,则需要一个本地变量
- JDK9不再要求定义临时变量,可以直接使用外部资源变量
// JDK7
FileInputStream fis = ....;
try (FileInputStream fis2 = fis) {
......
} catch (Exception ex) {
.......
}
// JDK9
FileInputStream fis = ....;
try (fis) {
.....
} catch (Exception ex) {
......
}
多个资源
try (FileInputStream fis = new FileInputStream(); FileOutputStream fos = new FileOutputStream) {
......
} catch (Exception ex) {
.......
}
ResourceBundle文件加载
- Java8及以前,ResourceBundle默认以ISO-8895-1方式加载Properties文件
- 需要利用native2ascii工具(JDK自带)对文件进行转义
- JDK9及以后,ResourceBundle默认以UTF-8方式加载Properties文件
- JDK9及以后,已经删除native2ascii工具
- 新的Properties文件可以直接以UTF-8保存
- 已利用native2ascii工具转化后的文件,不受影响.即ResourceBundle若解析文件不是有效的UTF-8,则以ISO-8895-1方式加载
var类型
- Java以前一直是一种强类型的编程语言
- 每个变量在定义是就确定了类型
- 类型固定了,就不能更改
- Java10推出了var: 局部变量推断
- 避免信息冗余
- 对齐了变量名
- 更容易阅读
- 本质上还是强类型语言,编译器负责推断类型,并写入字节码文件.因此推断后不能更改!!!
public static void main(String[] args) {
// current jdk13
int a1 = 5;
float a2 = 0.25f;
String a3 = "abc";
System.out.println(a1 + " " + a2 + " " + a3);
// 使用var类型
var b1 = 5;
var b2 = 0.25;
var b3 = "abc";
System.out.println(b1 + " " + b2 + " " + b3);
}
结果:
5 0.25 abc
5 0.25 abc
var的限制
- 可以使用在局部变量上,非类成员变量
- 可以用在for / for-each 循环中
- 声明时必须初始化
- 不能用在方式(形式)参数和返回类型
- 大面积滥用会使代码阅读性差
- var只在编译时起作用,没有在字节码中引入新的内容,也没有专门的JVM指令处理var
泛型
- 泛型:编写的代码可以被很多不同类型的对象所重用
- 泛型类: ArrayList , HashSet , HashMap等
- 泛型方法: Conllections.binarySearch , Arrays.sort等
- 泛型接口: List , Iterator等
泛型类
- 具有泛型变量的类
- 在类名后用<T>代表引入类型
- 多个字母表示多个引入类型,如<T,U>等
- 引入类型可以修饰成员变量 / 局部变量 / 参数 / 返回值
- ArrayList<E> E表示元素Element;
- HashMap<K,V> K表示Key, V表示Value
- 自定义泛型变量常用T, T表示Template
class Interval<T> {
private T lower;
private T upper;
public Interval(T lower, T upper) {
this.lower = lower;
this.upper = upper;
}
public T getLower() {
return lower;
}
}
泛型方法
- 具有泛型参数的方法
- 该方法可在普通类 / 泛型类中
- <T>在修饰符后,返回类型前
public class ArrayUtil {
/**
* <T>表示泛型方法的引入类型
* T... 本质就是一个数组
*
* @param a
* @return T
* */
public static <T> T getMinddle(T... a) {
return a[a.length/2];
}
}
public class Demo5 {
public static void main(String[] args) {
// 调用泛型方法时可以不指定类型
String s1 = ArrayUtil.getMinddle("abc", "def", "ghi");
// 调用泛型方法时指定对应的类型
Integer i1 = ArrayUtil.<Integer>getMinddle(1, 2, 3);
// 支持null
String s2 = ArrayUtil.<String>getMinddle("abc", "def", null);
// 传入的类型必须统一
Integer i2 = ArrayUtil.getMinddle(1, 2.5f, 3L); // error
}
}
泛型接口
- 和泛型类相似, 在类名后加<T>
- T用来指定方法返回值和参数
- 实现接口时, 指定类型
interface Calculator<T> {
public T add(T operand1, T operand2);
}
class IntegerCalculator implements Calculator<Integer> {
@Override
public Integer add(Integer operand1, Integer operand2) {
return operand1 + operand2;
}
}
- 当接口中的<T>本身也是一个泛型类时
class Interval<T> {
private T lower;
private T upper;
public Interval(T lower, T upper) {
this.lower = lower;
this.upper = upper;
}
public T getLower() {
return lower;
}
public T getUpper() {
return upper;
}
}
interface Calculator<T> {
public T add(T operand1, T operand2);
}
class IntegerCalculator implements Calculator<Interval<Integer>> {
@Override
public Interval<Integer> add(Interval<Integer> operand1, Interval<Integer> operand2) {
int lower = operand1.getLower() + operand2.getLower();
int upper = operand1.getUpper() + operand2.getUpper();
return new Interval<Integer>(lower, upper);
}
}
泛型的限定
- <T extends Comparable> 约定T必须是Comparable的子类
- extends固定, 后面可以多个, 以&拼接, 如<T extends Comparable & Serializable>
- extends限定可以有多个接口, 但只能一个类, 但类必须排第一位
- 逗号隔参, <T extends File & Cloneable, U extends Serializable>, 其中Cloneable和Serializable是接口
泛型之间的继承
- Pair<S>和Pair<T>没有任何关系, 无论S和T之间是什么关系
- 泛型类可以扩展或实现其他的类, 如ArrayList<T>实现List<T>