目录
语法糖定义:为了让事务更加容易读和理解的程序语言被成为语法糖。
一、for-each
for语句:
for(Iterator<Double> i = nums.iterator(); i.hasNext(); )
{
result += i.next();
}
for-each语句:
for(double item : nums)
{
result += item;
}
优点:
- 从jdk5开始引入
- 语言简洁、可以避免出现越界错误
相对于for语句的缺点:
- for语句可以删除元素,for-each不可以删除和替换
- for-each遍历时,不知道当前索引的位置
- for-each只能正向遍历,不能反向遍历
- for-each不能同时遍历多个集合
for和for-each性能比较接近,是同一个数量级的。
二、枚举
枚举:使变量取值只在一个有限的集合内。常用于性别,星期几,颜色等等
枚举的定义 :enum声明枚举类,枚举类都是Enum子类。
enum Size {
SMALL,MEDIUM,LARGE,EXTRA_LARGE;
}
枚举的实例化:枚举类中有多少个值就有多少个实例对象,不能直接new来实例。
枚举中的属性/构造函数/方法:枚举中还可以添加属性/构造函数/方法,但是枚举中的构造函数只能是default或者private属性,内部调用。
public class FruitTest {
public static void main(String[] args) {
Fruit a1 = Fruit.APPLE;
System.out.println("Price is " + a1.getPrice());
}
}
enum Fruit
{
APPLE(10), ORANGE(8);//将apple构造成10元
private int price;
Fruit(int price) {
this.price = price;
}
public int getPrice() {
return this.price;
}
}
输出:Price is 10
Enum类中的方法:
public class DayTest {
public static void main(String[] args) {
Day d1 = Day.MONDAY;
Day d2 = Enum.valueOf(Day.class, "MONDAY");
System.out.println(d1 == d2); //true
Day d3 = Enum.valueOf(Day.class, "TUESDAY");
System.out.println(d1.compareTo(d3)); //MONDAY<TUESDAY
//遍历所有的枚举值
for(Day item : Day.values())
{
System.out.println(item.toString() + "," + item.ordinal());
}
}
}
enum Day
{
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}
输出:
true
-1
MONDAY,0
TUESDAY,1
WEDNESDAY,2
THURSDAY,3
FRIDAY,4
SATURDAY,5
SUNDAY,6
三、不定项参数
表示可变参数,可以用于不确定同类型参数有几个的情况下。定义为在类型后面加...如:int.../double...(其实可以把不定项参数看成数组)
注意:一个方法只能有一个不定项参数,且必须位于参数列表的最后。
重载优先级:固定参数的方法比可变参数优先级高,调用语句,同时与两个带可变参数的方法匹配则会报错。
package sugar.variablearguments;
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)
// {
//
// }
}
输出:
0
I am another method
2
aaa
bbb
3
aaa
bbb
ccc
四、静态导入
import static导入一个类的静态方法和静态变量(jdk5以上)
相当于直接把方法导入不再需要以格式“类名.方法”调用,直接以格式:“方法”调用
注意:
- 静态导入时最好不要使用通配符*,最好具体到静态变量和方法。防止重复。
- 静态方法名具有明确特征,如有重名需要补充类名。
import static java.lang.Double.*;
import static java.lang.Math.*;
import static java.lang.Integer.*;
import static java.text.NumberFormat.*;
import java.text.NumberFormat;
public class BadImportStaticTest {
public static void main(String[] args) {
double s = PI * pow(parseDouble(args[0]),2);
NumberFormat nf = getInstance();
nf.setMaximumFractionDigits(parseInt(args[1]));
System.out.println("圆面积是:" + nf.format(s));
//System.out.println("最大值:" + MAX_VALUE); //因为java.lang.Double.*;java.lang.Integer.*;都含有MAX_VALUE静态变量,所以error
System.out.println("最大值:" + Integer.MAX_VALUE);
}
}
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);//普通导入
}
}
五、自动装箱与拆箱
目的:简化基本类型和对象之间的简化。
注意事项:
- 自动装箱拆箱时编译器的工作。
- ==判等时:基本类型判断内容是否相同,对象则判断指针是否指向统一内存区域
- 基本类型不能赋值为null,对象可以为null。
- 基础数据类型与封装类进行数据运算时,会自动给封装类进行拆箱,对基础数据类型进行运算。
- 谨慎使用多个非同类的数值类对象进行运算。
举例:
如果没有自动装箱拆箱:
ArrayList<Integer> list =new ArrayList<>();
list.add(Integer.valueof(2));
int a = list.get(1).intValue();
有自动装箱拆箱后:
ArrayList<Integer> list =new ArrayList<>();
list.add(2);
int a = list.get(1);
同时最好也注意一下常量池的概念:之前写过相关博客可见:https://blog.csdn.net/weixin_43698704/article/details/103946059
public class A {
public static void main(String[] args) {
Integer n1 =128;
Integer n2 =128;
System.out.println(n1==n2);
}
}
//输出false 涉及常量池
public class A {
public static void main(String[] args) {
int n1 =128;
int n2 =128;
System.out.println(n1==n2);
}
}
//输出true 所以敲代码最好用Int而不是integer
拆箱装箱示例:
public class NumberTest {
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); //拆箱再进行数值比较
System.out.println(a3 == (a1 + a2)); //拆箱再进行数值比较
System.out.println(a4 == (a1 + a2)); //拆箱再进行数值比较
System.out.println(a5 == (a1 + a2)); //拆箱再进行数值比较
System.out.println(a3.equals(a1+a2)); //equals要求同类,且内容相同
System.out.println(a4.equals(a1+a2)); //equals要求同类,且内容相同
System.out.println(a4.equals((long) (a1+a2))); //equals要求同类,且值相同
//System.out.println(a3 == a4); //不同类型不能比较,判断两个对象内存是否是同一个
}
}
输出:
true
true
true
true
true
false
true
六、多异常并列
多个异常并列放入一个catch中(jdk7以上)
注意:管道两侧异常类不可以有直接或间接继承关系。
try
{
test();
}
catch(IOException | SQLException ex)
{
//JDK7开始,支持一个catch写多个异常
//异常处理
}
七、数值赋值优化
(jdk7以上)
1.整数类型可以用二进制数赋值。
可用于:byte/short/int/long类型
2.在数字直接可以使用下划线_增加数字的可读性和纠错功能。
可用于short/int/long/float/double;
下划线只能出现在数字之间,前后必须有数字;
允许出现在2/8/10/16进制的数字中使用。
public class NumberTest {
public static void main(String[] args) {
long a1 = 9999999999L;
long a2 = 9_999_999_999L;
int a3 = 0b0111_1011_0001; //二进制, 0b开头
int a4 = 0_214; //八进制, 0开头
int a5 = 123___45; //可以多个下划线
int a6 = 0x7_B_1; //十六进制
float a7 = 3.56_78f; //float
double a8 = 1.3_45__67; //double
int b1 = 0b_123_4; //_必须在数字之间
int b2 = 0123_4_; //_不能在末尾
int b3 = _123; //_不能在开头
int b4 = 0_x_123; //不能拆开0x
int b5 = 0x_51; //_必须在数字之间
long b6 = 1000_L; //_必须在数字之间
float b7 = 1.34f_; //_不能在末尾
}
}
八、接口方法
之前也写过类似接口与抽象类的区别的博客可查看https://blog.csdn.net/weixin_43698704/article/details/103994472
最初接口内不可以让方法实现和公开,直到jdk8推出接口的默认方法/静态方法(都可以实现)。
默认方法:
Object方法有equal();toString();hashCode()(返回哈希码值);
规则五中如一个类引用的两个接口都含同名同参数的默认方法,需要在子类重写。可以用格式来重写:
public void move() {
NewAnimal.super.move(); //其中一个接口(父类)的move()方法
}
默认方法实现:
public interface NewAnimal {
public default void move()
{
System.out.println("I can move.");
}
}
静态方法:
定义:在方法前面加一个static
静态方法属于本接口,不属于子类或者子接口
子类(子接口)中没有继承静态方法,只能通过格式:“接口名.方法”来调用
public interface StaticAnimal {
public static void move()
{
System.out.println("I can move");
}
}
public interface StaticLandAnimal extends StaticAnimal {
//也继承不到StaticAnimal的move方法
}
public class StaticSwan implements StaticAnimal {
public static void main(String[] args) {
StaticAnimal.move();
//StaticLandAnimal.move(); //error,报错
//new StaticSwan().move(); //error,报错
}
}
私有方法:
(需要jdk9以上)
public interface PrivateAnimal {
public default void run()
{
move();//默认方法可以调用静态/非静态的私有方法
move2();
System.out.println("I can run");
}
public default void fly()
{
move();//默认方法可以调用静态/非静态的私有方法
System.out.println("I can fly");
}
private void move()
{
//非静态的私有方法
System.out.println("I can move");
System.out.println("I am growing");
}
public static void run2()
{
move2();//静态方法可以调用静态的私有方法
System.out.println("I can run");
}
public static void fly2()
{
move2();//静态方法可以调用静态的私有方法
System.out.println("I can fly");
}
private static void move2()
{
//静态的私有方法
System.out.println("I can move");
System.out.println("I am growing");
}
}
九、try-with-resource
程序如果打开外部资源,那么在使用结束以后需要正确关闭。
之前用的是try-catch-finally进行保证
public static void readFile1() {
FileInputStream fis = null;
InputStreamReader isr = null;
BufferedReader br = null;
try {
fis = new FileInputStream("c:/temp/abc.txt"); // 节点类
isr = new InputStreamReader(fis, "UTF-8"); // 转化类
//isr = new InputStreamReader(fis);
br = new BufferedReader(isr); // 装饰类
// br = new BufferedReader(new InputStreamReader(new
// FileInputStream("c:/temp/abc.txt")))
String line;
while ((line = br.readLine()) != null) // 每次读取一行
{
System.out.println(line);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
br.close(); // 关闭最后一个类,会将所有的底层流都关闭
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
jdk7提供try-with-resource,比上者更加方便。(资源对象需要实现了AutoCloseable接口,才可以用这个方法,fileInputStream中已经实现)
public class MyTryWithResourceTest {
public static void main(String[] args) {
//将会自动调用conn的close方法
try(MyConnection conn = new MyConnection())
{
conn.sendData();
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
class MyConnection implements AutoCloseable {
public void sendData() throws Exception {
System.out.println("Send Data....");
}
public void close() throws Exception {
System.out.println("Close....");
}
}
输出:
Send Data....
Close....
注意的是jdk7中需要在try()中定义资源,若在外面定义看,则需要一个本地变量。如:
jdk9不再要求定义临时变量,可以直接使用外部的资源变量。
public static void readFile2() {
String line;
//try-resource 语句,自动关闭资源
try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream("c:/temp/abc.txt")))) {
while ((line = in.readLine()) != null) {
System.out.println(line);
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
十、resource-bundle文件加载
jdk8及之前国际化编程中文件里面的编码都需要利用native2ascii工具进行转义,jdk9之后就不用了。
/**
* 测试ResourceBundle加载方式
* @author Tom
* 以JDK9及以上运行,程序正常,以JDK8运行,出现乱码,无法解析UTF-8文件
*/
public class NameTest {
public static void main(String[] args) {
Locale myLocale = Locale.getDefault();
System.out.println(myLocale); //zh_CN
// 根据指定语言_国家环境加载资源文件
ResourceBundle bundle = ResourceBundle.getBundle("msg", myLocale);
// 从资源文件中取得的消息
System.out.println(bundle.getString("name"));
}
}
输出:
zh_CN
吴琼
msg_zh_CN.properties,用UTF-8编码即可
#Update name name
#Wed Jul 31 15:26:28 CST 2019
name=吴琼
十一、var类型
jdk10推出了var,局部变量推断。
优点:
注意:本质上还是强类型语言,编译器负责推断类型,并写入字节码文件,因此推断后不能更改。
十二、switch
jdk12之前:
public static int judgeMonthDay(String month)
{
int result = 0;
switch(month)
{
case "Jan": case "Mar": case "May": case "July":
case "Aug": case "Oct": case "Dec":
result = 31;
break;
case "Apr": case "June": case "Sep": case "Nov":
result = 30;
break;
case "Feb":
result = 28;
break;
default:
result = -1;
}
return result;
}
jdk12及以后:
public static int judgeMonthDay12(String month)
{
//this method works based on Java 12.
//modify project properties/Java Compiler/Enable preview features
int result = 0;
//new switch, don't need break clauses
//-> 之后 : expression/block/throw
switch(month)
{
case "Jan","Mar","May","July","Aug","Oct","Dec" -> result = 31;
case "Apr","June","Sep","Nov" -> result = 30;
case "Feb" -> result = 28;
default -> result = -1;
}
return result;
}
可以直接有返回
public static void testSwitchReturn()
{
int num = 1;
int days = switch (num) {
case 1,3,5,7,8,10,12 -> 31;
case 4,6,9,11 -> 30;
default -> {
int result = 28;
break result; //代码块中break返回结果
}
};
System.out.println(days);
}
参考中国大学mooc《java核心技术》