常用记录
如何跳出当前的多重嵌套循环
方法一:break 带上标识
public static void method1() {
// 注意这里的标识
ok:
for(int i=0;i<10;i++){
for(int j=0;j<10;j++){
if(j==5){
//跳到循环外的ok出,即终止整个循环
break ok;
}
}
}
}
方法二:定义 boolean 变量判断是否跳出
public static void method2(){
int[][] arr = {{1,2,3},{4,5,6,7},{9}};
boolean found = false;
// 这里继续循环的条件是 i<arr.length && !found
for(int i=0;i<arr.length && !found;i++){
for(int j=0;j<arr[i].length;j++){
if(arr[i][j]==5){
//找到5,使外层循环判断条件变为false则终止整个循环
found = true;
//跳出当前循环
break;
}
}
}
}
拼接字符串
** 要高效拼接字符串,应该使用StringBuilder。**
String[] names = {"Bob", "Alice", "Grace"};
var sb = new StringBuilder();
sb.append("Hello ");
for (String name : names) {
sb.append(name).append(", ");
}
// 注意去掉最后的", ":
sb.delete(sb.length() - 2, sb.length());
sb.append("!");
System.out.println(sb.toString());
Java标准库还提供了一个StringJoiner来干这个事:
用StringJoiner的结果少了前面的"Hello "和结尾的"!"!遇到这种情况,需要给StringJoiner指定“开头”和“结尾”:
String[] names = {"Bob", "Alice", "Grace"};
var sj = new StringJoiner(", ", "Hello ", "!");
for (String name : names) {
sj.add(name);
}
System.out.println(sj.toString());
String还提供了一个静态方法join(),这个方法在内部使用了StringJoiner来拼接字符串,在不需要指定“开头”和“结尾”的时候,用String.join()更方便:
String[] names = {"Bob", "Alice", "Grace"};
var s = String.join(", ", names);
大数
在Java中,由CPU原生提供的整型。使用long型整数可以直接通过CPU指令进行计算,速度非常快。
如果我们使用的整数范围超过了long型(最大范围是64位long型整数)怎么办?java.math.BigInteger就是用来表示任意大小的整数。BigInteger内部用一个int[]数组来模拟一个非常大的整数:
对BigInteger做运算的时候,只能使用实例方法,例如,加法运算:
BigInteger i1 = new BigInteger("1234567890");
BigInteger i2 = new BigInteger("12345678901234567890");
BigInteger sum = i1.add(i2); // 12345678902469135780
BigInteger用于表示任意大小的整数;
BigInteger是不变类,并且继承自Number;
将BigInteger转换成基本类型时可使用longValueExact()等方法保证结果准确。
BigDecimal可以表示一个任意大小且精度完全准确的浮点数。
BigDecimal用scale()表示小数位数,例如:
BigDecimal d1 = new BigDecimal("123.45");
System.out.println(d1.scale()); // 2,两位小数
通过BigDecimal的stripTrailingZeros()方法,可以将一个BigDecimal格式化为一个相等的,但去掉了末尾0的BigDecimal:
BigDecimal d1 = new BigDecimal("123.4500");
BigDecimal d2 = d1.stripTrailingZeros();
System.out.println(d1.scale()); // 4
System.out.println(d2.scale()); // 2,因为去掉了00
BigDecimal d3 = new BigDecimal("1234500");
BigDecimal d4 = d3.stripTrailingZeros();
System.out.println(d3.scale()); // 0
System.out.println(d4.scale()); // -2
如果一个BigDecimal的scale()返回负数,例如,-2,表示这个数是个整数,并且末尾有2个0。
对BigDecimal做加、减、乘时,精度不会丢失,但是做除法时,存在无法除尽的情况,这时,就必须指定精度以及如何进行截断:
BigDecimal d1 = new BigDecimal("123.456");
BigDecimal d2 = new BigDecimal("23.456789");
BigDecimal d3 = d1.divide(d2, 10, RoundingMode.HALF_UP); // 保留10位小数并四舍五入
BigDecimal d4 = d1.divide(d2); // 报错:ArithmeticException,因为除不尽
** 比较BigDecimal的值是否相等,必须使用compareTo()而不能使用equals()。**
SecureRandom:生成安全的随机数
集合
创建List 除了使用ArrayList和LinkedList,我们还可以通过List接口提供的of()方法,根据给定元素快速创建List:
List list = List.of(1, 2, 5);
但是List.of()方法不接受null值,如果传入null,会抛出NullPointerException异常。
Iterator对象实现也是不同的,但总是具有最高的访问效率。
Java的for each循环本身就可以帮我们使用Iterator遍历
使用EnumMap
如果Map的key是enum类型,推荐使用EnumMap,既保证速度,也不浪费空间。
import java.time.DayOfWeek;
import java.util.*;
public class Main {
public static void main(String[] args) {
Map<DayOfWeek, String> map = new EnumMap<>(DayOfWeek.class);
map.put(DayOfWeek.MONDAY, "星期一");
map.put(DayOfWeek.TUESDAY, "星期二");
map.put(DayOfWeek.WEDNESDAY, "星期三");
map.put(DayOfWeek.THURSDAY, "星期四");
map.put(DayOfWeek.FRIDAY, "星期五");
map.put(DayOfWeek.SATURDAY, "星期六");
map.put(DayOfWeek.SUNDAY, "星期日");
System.out.println(map);
System.out.println(map.get(DayOfWeek.MONDAY));
}
}
TreeMap
还有一种Map,它在内部会对Key进行排序,这种Map就是SortedMap。注意到SortedMap是接口,它的实现类是TreeMap。
使用TreeMap时,放入的Key必须实现Comparable接口。String、Integer这些类已经实现了Comparable接口,因此可以直接作为Key使用。作为Value的对象则没有任何要求。如果作为Key的class没有实现Comparable接口,那么,必须在创建TreeMap时同时指定一个自定义排序算法:
public class Main {
public static void main(String[] args) {
Map<Student, Integer> map = new TreeMap<>(new Comparator<Student>() {
public int compare(Student p1, Student p2) {
if (p1.score == p2.score) {
return 0;
}
return p1.score > p2.score ? -1 : 1;
}
});
map.put(new Student("Tom", 77), 1);
map.put(new Student("Bob", 66), 2);
map.put(new Student("Lily", 99), 3);
for (Student key : map.keySet()) {
System.out.println(key);
}
System.out.println(map.get(new Student("Bob", 66))); // null?
}
}
class Student {
public String name;
public int score;
Student(String name, int score) {
this.name = name;
this.score = score;
}
public String toString() {
return String.format("{%s: score=%d}", name, score);
}
}
Properties
用Properties读取配置文件,一共有三步:
创建Properties实例; 调用load()读取文件;
调用getProperty()获取配置。
写入配置文件
Properties props = new Properties();
props.setProperty("url", "http://www.liaoxuefeng.com");
props.setProperty("language", "Java");
props.store(new FileOutputStream("C:\\conf\\setting.properties"), "这是写入的properties注释");
读取配置文件
可以从文件系统读取这个.properties文件:
String f = "setting.properties";
Properties props = new Properties();
props.load(new java.io.FileInputStream(f));
String filepath = props.getProperty("last_open_file");
String interval = props.getProperty("auto_save_interval", "120");
可以从classpath读取.properties文件,因为load(InputStream)方法接收一个InputStream实例,表示一个字节流,它不一定是文件流,也可以是从jar包中读取的资源流:
Properties props = new Properties();
props.load(getClass().getResourceAsStream("/common/setting.properties"));
由于load(InputStream)默认总是以ASCII编码读取字节流,所以会导致读到乱码。我们需要用另一个重载方法load(Reader)读取:
Properties props = new Properties();
props.load(new FileReader("settings.properties", StandardCharsets.UTF_8));
PriorityQueue优先队列
从队首获取元素时,总是获取优先级最高的元素。
PriorityQueue默认按元素比较的顺序排序(必须实现Comparable接口),也可以通过Comparator自定义排序算法(元素就不必实现Comparable接口)。
放入PriorityQueue的元素,必须实现Comparable接口,PriorityQueue会根据元素的排序顺序决定出队的优先级。
如果我们要放入的元素并没有实现Comparable接口怎么办?PriorityQueue允许我们提供一个Comparator对象来判断两个元素的顺序。我们以银行排队业务为例,实现一个PriorityQueue:
public class Main {
public static void main(String[] args) {
Queue<User> q = new PriorityQueue<>(new UserComparator());
// 添加3个元素到队列:
q.offer(new User("Bob", "A1"));
q.offer(new User("Alice", "A2"));
q.offer(new User("Boss", "V1"));
System.out.println(q.poll()); // Boss/V1
System.out.println(q.poll()); // Bob/A1
System.out.println(q.poll()); // Alice/A2
System.out.println(q.poll()); // null,因为队列为空
}
}
class UserComparator implements Comparator<User> {
public int compare(User u1, User u2) {
if (u1.number.charAt(0) == u2.number.charAt(0)) {
// 如果两人的号都是A开头或者都是V开头,比较号的大小:
return u1.number.compareTo(u2.number);
}
if (u1.number.charAt(0) == 'V') {
// u1的号码是V开头,优先级高:
return -1;
} else {
return 1;
}
}
}
class User {
public final String name;
public final String number;
public User(String name, String number) {
this.name = name;
this.number = number;
}
public String toString() {
return name + "/" + number;
}
}
使用Files
Files是java.nio包里面的类,封装了很多读写文件的简单方法,
例如,我们要把一个文件的全部内容读取为一个byte[],可以这么写:
byte[] data = Files.readAllBytes(Path.of("/path/to/file.txt"));
如果是文本文件,可以把一个文件的全部内容读取为String:
// 默认使用UTF-8编码读取:
String content1 = Files.readString(Path.of("/path/to/file.txt"));
// 可指定编码:
String content2 = Files.readString(Path.of("/path", "to", "file.txt"), StandardCharsets.ISO_8859_1);
// 按行读取并返回每行内容:
List<String> lines = Files.readAllLines(Path.of("/path/to/file.txt"));
写入文件也非常方便:
// 写入二进制文件:
byte[] data = ...
Files.write(Path.of("/path/to/file.txt"), data);
// 写入文本并指定编码:
Files.writeString(Path.of("/path/to/file.txt"), "文本内容...", StandardCharsets.ISO_8859_1);
// 按行写入文本:
List<String> lines = ...
Files.write(Path.of("/path/to/file.txt"), lines);
注意的是,Files提供的读写方法,受内存限制,只能读写小文件,例如配置文件等,不可一次读入几个G的大文件。读写大型文件仍然要使用文件流,每次只读写一部分文件内容。
Date
java.util.Date是用于表示一个日期和时间的对象
public class Main {
public static void main(String[] args) {
// 获取当前时间:
Date date = new Date();
System.out.println(date.getYear() + 1900); // 必须加上1900
System.out.println(date.getMonth() + 1); // 0~11,必须加上1
System.out.println(date.getDate()); // 1~31,不能加1
// 转换为String:
System.out.println(date.toString());
// 转换为GMT时区:
System.out.println(date.toGMTString());
// 转换为本地时区:
System.out.println(date.toLocaleString());
}
}
LocalDateTime
从Java 8开始,java.time包提供了新的日期和时间API,主要涉及的类型有:
本地日期和时间:LocalDateTime,LocalDate,LocalTime; 带时区的日期和时间:ZonedDateTime;
时刻:Instant; 时区:ZoneId,ZoneOffset; 时间间隔:Duration。
以及一套新的用于取代SimpleDateFormat的格式化类型 DateTimeFormatter。
java.time中以Instant类型表示,我们用Instant.now()获取当前时间戳,效果和System.currentTimeMillis()类似:
Instant now = Instant.now();
System.out.println(now.getEpochSecond()); // 秒
System.out.println(now.toEpochMilli()); // 毫秒
** 具体: ** 添加链接描述
java主线程捕获子线程中的异常
package com.xueyou.demo.theadexceptiondemo;
import com.sun.glass.ui.TouchInputSupport;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class ThreadExceptionDemo {
public static void main(String[] args) {
try {
Thread thread = new Thread(new ThreadExceptionRunner());
thread.start();
} catch (Exception e) {
System.out.println("========");
e.printStackTrace();
} finally {
}
System.out.println(123);
ExecutorService exec = Executors.newCachedThreadPool(new HandleThreadFactory());
exec.execute(new ThreadExceptionRunner());
exec.shutdown();
}
}
class MyUncaughtExceptionHandle implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("caught " + e);
}
}
class HandleThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
System.out.println("create thread t");
Thread t = new Thread(r);
System.out.println("set uncaughtException for t");
t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandle());
return t;
}
}
上面的方式是设置每一个线程执行时候的异常处理。如果每一个线程的异常处理相同,我们可以用如下的方式进行处理,使用Thread的静态方法。
Thread.setDefaultUncaughtExceptionHandler(new
MyUncaughtExceptionHandle());
package com.xueyou.demo.theadexceptiondemo;
import com.sun.glass.ui.TouchInputSupport;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
/**
* Created by wuxueyou on 2018/6/24.
*/
public class ThreadExceptionDemo {
public static void main(String[] args) {
try {
Thread thread = new Thread(new ThreadExceptionRunner());
thread.start();
} catch (Exception e) {
System.out.println("========");
e.printStackTrace();
} finally {
}
System.out.println(123);
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandle());
// ExecutorService exec = Executors.newCachedThreadPool(new HandleThreadFactory());
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ThreadExceptionRunner());
exec.shutdown();
}
}
ProcessBuilder执行命令
ProcessBuilderbuilder不支持包含空格的命令。
String cmd = "cp";
// 命令的各个部分组成一个字符串数组,用该数组创建ProcessBuilder对象
String[] cmds = new String[] {cmd, "-rf", src, tag};
ProcessBuilder builder = new ProcessBuilder(cmds);
try {
Process process = builder.start();
process.waitFor();
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.toString());
}
Java执行cmd命令
一般java在执行CMD命令时,通常是使用Runtime.getRuntime.exec(command)来执行的
public static void aaa(String stam){
BufferedReader br = null;
try {
File file = new File("D:\\daemonTmp");
File tmpFile = new File("D:\\daemonTmp\\temp.tmp");//新建一个用来存储结果的缓存文件
if (!file.exists()){
file.mkdirs();
}
if(!tmpFile.exists()) {
tmpFile.createNewFile();
}
ProcessBuilder pb = new ProcessBuilder().command("cmd.exe", "/c", stam).inheritIO();
pb.redirectErrorStream(true);//这里是把控制台中的红字变成了黑字,用通常的方法其实获取不到,控制台的结果是pb.start()方法内部输出的。
pb.redirectOutput(tmpFile);//把执行结果输出。
pb.start().waitFor();//等待语句执行完成,否则可能会读不到结果。
InputStream in = new FileInputStream(tmpFile);
br= new BufferedReader(new InputStreamReader(in));
String line = null;
while((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
br = null;
tmpFile.delete();//卸磨杀驴。
System.out.println("执行完成");
} catch (Exception e) {
e.printStackTrace();
} finally {
if(br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// An highlighted block
public void executeBat() {
try {
Runtime run = Runtime.getRuntime();
String cmd = "E:\\Re-Architecture\\test.bat"
Process pro = run.exec(cmd);
InputStream ers= pro.getErrorStream();
pro.waitFor();
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
获取运行jar的路径
URL location = MethodHsdk.class.getProtectionDomain().getCodeSource().getLocation();
String decode = URLDecoder.decode(location.getPath(), "utf-8");//转化为utf-8编码
http常见的状态码
下面给出一些状态码的适用场景:
100:客户端在发送POST数据给服务器前,征询服务器情况,看服务器是否处理POST的数据,如果不处理,客户端则不上传POST数据,如果处理,则POST上传数据。常用于POST大数据传输
206:一般用来做断点续传,或者是视频文件等大文件的加载
301:永久重定向会缓存。新域名替换旧域名,旧的域名不再使用时,用户访问旧域名时用301就重定向到新的域名
302:临时重定向不会缓存,常用 于未登陆的用户访问用户中心重定向到登录页面
304:协商缓存,告诉客户端有缓存,直接使用缓存中的数据,返回页面的只有头部信息,是没有内容部分
400:参数有误,请求无法被服务器识别
403:告诉客户端禁止访问该站点或者资源,如在外网环境下,然后访问只有内网IP才能访问的时候则返回
404:服务器找不到资源时,或者服务器拒绝请求又不想说明理由时
503:服务器停机维护时,主动用503响应请求或 nginx 设置限速,超过限速,会返回503
504:网关超时
数组转成 List
最原始的方式,就是通过遍历数组的方式,一个个将数组添加到 List 中。
int[] anArray = new int[] {1, 2, 3, 4, 5};
List<Integer> aList = new ArrayList<>();
for (int element : anArray) {
aList.add(element);
}
更优雅的方式是通过 Arrays 类的 asList() 方法:
List<Integer> aList = Arrays.asList(anArray);
不过需要注意的是,Arrays.asList 的参数需要是 Integer 数组
还需要注意的是
Arrays.asList 方法返回的 ArrayList 并不是 java.util.ArrayList,它其实是 Arrays 类的一个内部类
如果需要添加元素或者删除元素的话,需要把它转成 java.util.ArrayList。
new ArrayList<>(Arrays.asList(anArray));
换另外一种方式
List<Integer> aList = Arrays.stream(anArray).boxed().collect(Collectors.toList());
Java 8 新增了 Stream 流的概念,这就意味着我们也可以将数组转成 Stream 进行操作。
String[] anArray = new String[] {"沉默王二", "一枚有趣的程序员", "好好珍重他"};
Stream<String> aStream = Arrays.stream(anArray);
如果想对数组进行排序的话,可以使用 Arrays 类提供的 sort() 方法。