for
VS for-each
优点:
- for-each 1从JDK5.0开始引入
- for-each 语法更简洁
- for-each 避免越界错误
缺点:
- for 可以删除元素,for-each不可以删除/替换元素
- for-each遍历的时候,是不知道当前元素的具体位置索引
- for-each只能正向遍历,不能反向遍历
- for-each不能同时遍历2个集合
for和for-each性能接近。
枚举类型
枚举变量:变量的取值只在一个有限的集合内。
enum Weekday {
MON(1), TUE(2), WED(3), THU(4), FRI(5), SAT(6), SUN(0);
public final int dayValue;
private Weekday(int dayValue) {
this.dayValue = dayValue;
}
}
参考链接:https://www.liaoxuefeng.com/wiki/1252599548343744/1260473188087424
可变参数
可变参数用类型...
定义,可变参数相当于数组类型:
class Group {
private String[] names;
public void setNames(String... names) {
this.names = names;
}
}
上面的setNames()就定义了一个可变参数。调用时,可以这么写:
Group g = new Group();
g.setNames("Xiao Ming", "Xiao Hong", "Xiao Jun"); // 传入3个String
g.setNames("Xiao Ming", "Xiao Hong"); // 传入2个String
g.setNames("Xiao Ming"); // 传入1个String
g.setNames(); // 传入0个String
- 一个方法只能有一个不定项参数,且必须位于参数列表的最后。
- 重载的优先级规则1:固定参数的方法,比可变参数优先级更高。
- 重载的优先级规则2:调用语句,同时与两个带可变参数的方法匹配,则报错。
public class VariableArgumentTest {
public static void main(String[] args) {
print();
print("aaa");
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);
}
}
//当只有一个参数时,本方法优先级更高
public static void print(String s)
{
System.out.println("I am another method");
}
//错误:一个方法不可以有多个可变参数
// public static void print(String... args, int... irgs)
// {
//
// }
//错误:一个调用语句不能同时有2个带可变参数的方法适配
// public static void print(String s1, String... args)
// {
//
// }
}
静态导入
import static
导入一个类的静态方法和静态变量(JDK5引入)。
import static java.lang.Math.pow;
import static java.lang.Math.sqrt;
import static java.lang.System.*;
import static java.lang.System.out;
public class ImportStaticTest {
public static void importMath() {
int a=3, b=4, c=0;
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);
}
}
自动拆箱和装箱
自动装箱和拆箱(auto-boxing/auto-unboxing)
从JDK5.0开始引入,简化基本类型和对象转换的写法:
基本类型:boolean/byte/char/int/short/long/float/double
对象:Boolean/Byte/Character/Integer/Short/Long/Float/Double:
import java.util.ArrayList;
public class IntegerTest {
public static void main(String[] args)
{
Integer obj1 = 5; //自动装箱
Integer obj2 = Integer.valueOf(5);
int a1 = obj1; //自动拆箱
int a2 = obj1.intValue();
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(Integer.valueOf(2));
int a3 = list.get(1);
int a4 = list.get(1).intValue();
}
}
自动装箱和拆箱的注意事项:
装箱和拆箱是编译器的工作,在class中已经添加转化。虚拟机没有自动装箱和拆箱的语句。
基本类型没有空值,对象有null,可能触发NullPointerException。
当一个基础数据类型与封装类进行==
、+
、-
、*
、/运算时,会将封装类进行拆箱,对基础数据类型进行运算。
接口方法
- Java最初的设计中,接口的方法都是没有实现的、公开的
public interface Animal {
public void move();
}
- Java 8推出接口的默认方法/静态方法(都带实现的)。
public interface NewAnimal {
public default void move(){
System.out.println("I can move");
}
}
接口的默认方法:
- 以
default
关键字标注,其他的定义和普通函数一 - 规则1:默认方法不能重写Object中的方法
- 规则2:实现类可以继承/重写父接口的默认方法
- 规则3:接口可以继承/重写父接口的默认方法规则4:当父类和父接口都有(同名同参数)默认方法,子类继承父类的默认方法,这样可以兼容JDK7及以前的代码
- 规则5:子类实现了2个接口(均有同名同参数的默认方法),那么编译失败,必须在子类中重写这个default方法
接口的静态方法:
Java 8接口的静态方法 (带实现的)
- 该静态方法属于本接口的,不属于子类/子接口
- 子类(子接口)没有继承该静态方法,只能通过所在的接口名来调用
接口的私有方法:
- 解决多个默认方法/静态方法的内容重复问题
- 私有方法属于本接口,只在本接口内使用,不属于子类/子接口
- 子类(子接口)没有继承该私有方法,也无法调用
- 静态私有方法可以被静态/默认方法调用,非静态私有方法只能被默认方法调用
var类型
Java 10推出var:局部变量推断
- 避免信息冗余
- 对齐了变量名
- 更容易阅读
- 本质上还是强类型语言,编译器负责推断类型,并写入字节码文件。因此推断后不能更改!!!
var的限制:
- 可以用在局部变量上,非类成员变量
- 可以用在for/for-each循环中
- 声明时必须初始化
- 不能用在方法(形式)参数和返回类型
- 大面积滥用会使代码整体阅读性变差
- var只在编译时起作用,没有在字节码中引入新的内容,也没有专门的JVM指令处理var。
import java.io.*;
import java.net.*;
public class VarTest {
public static void main(String[] args) throws Exception {
var b1 = 5;
var b2 = 0.25;
var b3 = "abc";
var b4 = new URL("https://github.com/");
//var b0; //error
//b3 = 5; //error
//var b5=5,b6=6,b7=7; //error
//var b8=null; //error
int[] nums = new int[3];
//var nums2 = {0,1,2}; error
for(var i=0;i<nums.length;i++)
{
nums[i] = i*2;
}
for(var i:nums)
{
System.out.println(i);
}
//var可以和三元操作符配合
var a5 = a1>b1? 10:15;
var b5 = a1+2<=b1? a1+10:b3;
System.out.println(b5.getClass().getName());
//var对继承有效
//子类型var可以赋值给父类型的var
var obj1 = new Father();
var obj2 = new Son();
obj1 = obj2;
}
}