连续赋值
比如: a = b = true;
关于新增元素到set的一些用法:
如果新增的元素是基本类型,那么会自动判断相等。把重复的元素干掉。
如果新增的元素是复杂类型,比如数组,对象。那么判断相等的条件是该对象的内存地址。如果加入的是同一变量,那么该变量只会被加入一次。也就是说加入的时候判断相等的条件是该对象的内存地址。
public static void main(String[] args) {
Set<int[]> set = new HashSet<>();
int[] item = new int[]{1, 2};
int[] item2 = item;
set.add(item);
set.add(item2);
for (int[] arr : set) {
System.out.println(arr[0] + "," + arr[1]);
}
// 输出:1,2
}
特别的,如果是基本类型的封装类呢?做个试验看看!
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(new Integer(2));
set.add(new Integer(2));
for (int item : set) {
System.out.println(item);
}
// 输出: 2
}
显然,和基本类型是一样的。
复制List<T> 类型
public static void main(String[] args) {
List<List<Integer>> container = new ArrayList<>();
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(4);
container.add(list);
// 输出 3 4
systemContainer(container);
list.remove(1);
// 输出 3
systemContainer(container);
}
public static void systemContainer(List<List<Integer>> container) {
for (List<Integer> list : container) {
for (int item : list) {
System.out.print(item + " ");
}
System.out.println();
}
}
显然,如果直接把list加到container中。由于是浅复制,加入到container中的是list的内存地址。所以list改变时,container中跟着改变。那么深复制该怎么做呢?很简单:
public static void main(String[] args) {
List<List<Integer>> container = new ArrayList<>();
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(4);
container.add(new ArrayList<>(list));
// 输出 3 4
systemContainer(container);
list.remove(1);
// 输出 3 4
systemContainer(container);
}
在new ArrayList<>(list)的时候,可以看到源码,用到了Arrays.copy(…)。显然这是一个深复制。
如果List<Integer>中的Integer换成复杂类型呢?试验看看:
class Stu {
String name;
public int age;
Stu(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + "--" + age;
}
}
public class MyClass {
public static void main(String[] args) {
List<List<Stu>> container = new ArrayList<>();
List<Stu> list = new ArrayList<>();
list.add(new Stu("zhang", 3));
list.add(new Stu("Li", 4));
container.add(new ArrayList<>(list));
// 输出 zhang--3 Li--4
systemContainer(container);
list.remove(1);
// 输出 zhang--3 Li--4
systemContainer(container);
list.get(0).age = 10;
// 输出 zhang--10 Li--4
systemContainer(container);
}
public static void systemContainer(List<List<Stu>> container) {
for (List<Stu> list : container) {
for (Stu item : list) {
System.out.print(item + " ");
}
System.out.println();
}
}
}
从上面的例子看出,如果List中的泛型是复杂类型的话,用 new ArrayList<>(list) 并没有深拷贝,仅仅是把list中所有对象的内存地址给到新建的ArrayList中。这也符合我们的预期。如果确实想要深拷贝,那么需要自己写拷贝函数。
复制一个数组
public static void main(String[] args) {
String[] array2 = new String[]{"aa", "bb", "cc"};
// 如果是简单类型数组,深拷贝。如果是复杂类型数组,浅拷贝
String[] arr3 = Arrays.copyOf(array2, array2.length);
}
那么如果这个数组是二维数组呢?
class Solution {
public static void main(String[] args) {
int[][] a = new int[3][3];
// 这里的长度可以比原来长,也可以比原来短(截取前面的元素)
int[][] b = Arrays.copyOf(a, a.length + 1);
a[0][0] = 10;
int[] a1 = {1, 2, 3};
b[3] = a1;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 3; ++j) {
System.out.print(b[i][j] + " ");
}
System.out.println();
}
/** 输出
* 10 0 0
* 0 0 0
* 0 0 0
* 1 2 3
*/
}
}
可以看到,如果是复杂类型数组,是浅复制。二维数组本质是一个一维数组,只不过数组类型是一个一维数组。
复制一个字符串n次返回新串
如果是java10的话,String类有一个repeat方法可以直接用。但是鉴于大部分是java8,所以可以用下面的代码。
String s = "Abc";
int n = 4;
String ret = String.join("", Collections.nCopies(n, s));
nCopies函数返回一个拥有n个s元素的列表。join表示把列表组合成一个字符串,中间的分隔符为空串,这样就曲线救国实现了复制字符串n次。
创建一个元素类型是List的数组
List<Integer>[] tower = new ArrayList[3];
上面的创建方法会报一个安全警告,不用管它。
保留两位小数
class Solution {
public static void main(String[] args) {
double d = 2.344;
// 保留两位小数,多余位直接舍去
System.out.println(((int)(d * 100)) / 100.0);
// 保留两位小数,四舍五入
System.out.println(((int)(d * 100 + 0.5)) / 100.0);
}
}
Arrays.sort自定义排序
import java.util.*;
public class Main {
public static void main(String[] args){
//不能使用基本数据类型
Integer[] arr = {5,4,7,9,2,12,54,21,1};
//降序
Arrays.sort(arr, (a, b) -> {
//返回值>0交换
return b-a;
});
System.out.println(Arrays.toString(arr));
}
}
List和Array互相转换
public static void main(String[] args) {
List<String> testList = new ArrayList<String>(){{add("aa");add("bb");add("cc");}};
String[] array2 = testList.toArray(new String[0]);
// 直接用Arrays.asList转成的List不能增删元素。两者具体区别可百度
List<String> list = new ArrayList<>(Arrays.asList(array2));
// 还可以这么转
List<int[]> ret = new ArrayList<>();
int[][] tp = ret.toArray(new int[0][0]);
}
定义常量
final double PI = 3.14; // PI是一个常量
double r = 5.0;
double area = PI * r * r;
PI = 300; // compile error!
多行字符串
如果我们要表示多行字符串,使用+号连接会非常不方便:
String s = "first line \n"
+ "second line \n"
+ "end";
从Java 13开始,字符串可以用"""…"""表示多行字符串(Text Blocks)了。举个例子:
public class Main {
public static void main(String[] args) {
String s = """
SELECT * FROM
users
WHERE id > 100
ORDER BY name DESC
""";
System.out.println(s);
}
}
上述多行字符串实际上是5行,在最后一个DESC后面还有一个\n。如果我们不想在字符串末尾加一个\n,就需要这么写:
String s = “”"
SELECT * FROM
users
WHERE id > 100
ORDER BY name DESC""";
还需要注意到,多行字符串前面共同的空格会被去掉,即:
String s = “”"
…SELECT * FROM
… users
…WHERE id > 100
…ORDER BY name DESC
…""";
用.标注的空格都会被去掉。
如果多行字符串的排版不规则,那么,去掉的空格就会变成这样:
String s = “”"
… SELECT * FROM
… users
…WHERE id > 100
… ORDER BY name DESC
… “”";
即总是以最短的行首空格为基准。
输入输出
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); // 创建Scanner对象
System.out.print("Input your name: "); // 打印提示
String name = scanner.nextLine(); // 读取一行输入并获取字符串
System.out.print("Input your age: "); // 打印提示
int age = scanner.nextInt(); // 读取一行输入并获取整数
System.out.printf("Hi, %s, you are %d\n", name, age); // 格式化输出
}
}
switch case的用法
public class Main {
public static void main(String[] args) {
String fruit = "apple";
switch (fruit) {
case "apple":
System.out.println("Selected apple");
break;
case "pear":
System.out.println("Selected pear");
break;
case "mango":
System.out.println("Selected mango");
break;
default:
System.out.println("No fruit selected");
break;
}
}
}
注意:不要忘记break,不要忘记default.
java12 升级版switch
使用switch时,如果遗漏了break,就会造成严重的逻辑错误,而且不易在源代码中发现错误。从Java 12开始,switch语句升级为更简洁的表达式语法,使用类似模式匹配(Pattern Matching)的方法,保证只有一种路径会被执行,并且不需要break语句:
public class Main {
public static void main(String[] args) {
String fruit = "apple";
switch (fruit) {
case "apple" -> System.out.println("Selected apple");
case "pear" -> System.out.println("Selected pear");
case "mango" -> {
System.out.println("Selected mango");
System.out.println("Good choice!");
}
default -> System.out.println("No fruit selected");
}
}
}
注意新语法使用->,如果有多条语句,需要用{}括起来。不要写break语句,因为新语法只会执行匹配的语句,没有穿透效应。
很多时候,我们还可能用switch语句给某个变量赋值。例如:
int opt;
switch (fruit) {
case "apple":
opt = 1;
break;
case "pear":
case "mango":
opt = 2;
break;
default:
opt = 0;
break;
}
使用新的switch语法,不但不需要break,还可以直接返回值。把上面的代码改写如下:
public class Main {
public static void main(String[] args) {
String fruit = "apple";
int opt = switch (fruit) {
case "apple" -> 1;
case "pear", "mango" -> 2;
default -> 0;
}; // 注意赋值语句要以;结束
System.out.println("opt = " + opt);
}
}
yield
大多数时候,在switch表达式内部,我们会返回简单的值。
但是,如果需要复杂的语句,我们也可以写很多语句,放到{…}里,然后,用yield返回一个值作为switch语句的返回值:
public class Main {
public static void main(String[] args) {
String fruit = "orange";
int opt = switch (fruit) {
case "apple" -> 1;
case "pear", "mango" -> 2;
default -> {
int code = fruit.hashCode();
yield code; // switch语句返回值
}
};
// opt = -1008851410
System.out.println("opt = " + opt);
}
}
从Java 14开始,switch语句正式升级为表达式,不再需要break,并且允许使用yield返回值。
字符串和数据类型互转
字符串转int
Integer.parseInt(str)
int 转字符串
Integer.toString(number)
StringBuffer和StringBuilder类
上述两种字符串可以被修改,不产生新的字符串。StringBuilder不是线程安全的。速度快。StringBuffer线程安全,速度慢。
StringBuffer常用方法:
StringBuffer sb = new StringBuffer(“abcd”)
sb.append(“aaa”)
sb.reverse()
sb.delete(2, 3) // delete(int start, int end) 左闭右开
deleteCharAt(int index)
sb.insert(4, “java”)
sb.replace(2, 4, “java”) // end字符不会被替换
sb.toString()
StringBuilder 同理