黑马程序员技术交流社区

标题: 【上海校区】到头来还是逃不开Java - Java13核心类 [打印本页]

作者: 梦缠绕的时候    时间: 2020-2-17 10:04
标题: 【上海校区】到头来还是逃不开Java - Java13核心类
引言#
兜兜转转到了大四,学过了C,C++,C#,Java,Python,学一门丢一门,到了最后还是要把Java捡起来。所以奉劝大家,面向对象还是要掌握一门,虽然Python好写舒服,但是毕竟不能完全面向对象,也没有那么多的应用场景,所以,奉劝看到本文的各位,还是提前学好C#或者Java。
字符串和编码#String#Copy
public class Main {    public static void main(String[] args) {        String s = "Hello";        System.out.println(s);        s = s.toUpperCase();        System.out.println(s);    }}字符串比较#Copy
public class Main {    public static void main(String[] args) {        String s1 = "hello";        String s2 = "hello";        System.out.println(s1 == s2);        System.out.println(s1.equals(s2));    }}Copy
public class Main {    public static void main(String[] args) {        String s1 = "hello";        String s2 = "HELLO".toLowerCase();        System.out.println(s1 == s2);        System.out.println(s1.equals(s2));    }}Copy
// 是否包含子串:"Hello".contains("ll"); // trueCopy
"Hello".indexOf("l"); // 2"Hello".lastIndexOf("l"); // 3"Hello".startsWith("He"); // true"Hello".endsWith("lo"); // trueCopy
"Hello".substring(2); // "llo""Hello".substring(2, 4); "ll"去除首尾空白字符#Copy
"  \tHello\r\n ".trim(); // "Hello"Copy
"\u3000Hello\u3000".strip(); // "Hello"" Hello ".stripLeading(); // "Hello "" Hello ".stripTrailing(); // " Hello"Copy
"".isEmpty(); // true,因为字符串长度为0"  ".isEmpty(); // false,因为字符串长度不为0"  \n".isBlank(); // true,因为只包含空白字符" Hello ".isBlank(); // false,因为包含非空白字符替换子串#Copy
String s = "hello";s.replace('l', 'w'); // "hewwo",所有字符'l'被替换为'w's.replace("ll", "~~"); // "he~~o",所有子串"ll"被替换为"~~"Copy
String s = "A,,B;C ,D";s.replaceAll("[\\,\\;\\s]+", ","); // "A,B,C,D"分割字符串#Copy
String s = "A,B,C,D";String[] ss = s.split("\\,"); // {"A", "B", "C", "D"}拼接字符串#Copy
String[] arr = {"A", "B", "C"};String s = String.join("***", arr); // "A***B***C"类型转换#Copy
String.valueOf(123); // "123"String.valueOf(45.67); // "45.67"String.valueOf(true); // "true"String.valueOf(new Object()); // 类似java.lang.Object@636be97cCopy
int n1 = Integer.parseInt("123"); // 123int n2 = Integer.parseInt("ff", 16); // 按十六进制转换,255Copy
boolean b1 = Boolean.parseBoolean("true"); // trueboolean b2 = Boolean.parseBoolean("FALSE"); // falseCopy
Integer.getInteger("java.version"); // 版本号,11转换为char[]#Copy
char[] cs = "Hello".toCharArray(); // String -> char[]String s = new String(cs); // char[] -> StringCopy
public class Main {    public static void main(String[] args) {        char[] cs = "Hello".toCharArray();        String s = new String(cs);        System.out.println(s);        cs[0] = 'X';        System.out.println(s);    }}
从String的不变性设计可以看出,如果传入的对象有可能改变,我们需要复制而不是直接引用。
Copy
public class Main {    public static void main(String[] args) {        int[] scores = new int[] { 88, 77, 51, 66 };        Score s = new Score(scores);        s.printScores();        scores[2] = 99;        s.printScores();    }}class Score {    private int[] scores;    public Score(int[] scores) {        // 这样传入的就是scores的复制        this.scores = Arrays.copyOf(scores, scores.length);        // 使用如下方法也可以        // this.scores = scores.clone();    }    public void printScores() {        System.out.println(Arrays.toString(scores));    }}字符编码#Copy
byte[] b1 = "Hello".getBytes(); // 按ISO8859-1编码转换,不推荐byte[] b2 = "Hello".getBytes("UTF-8"); // 按UTF-8编码转换byte[] b2 = "Hello".getBytes("GBK"); // 按GBK编码转换byte[] b3 = "Hello".getBytes(StandardCharsets.UTF_8); // 按UTF-8编码转换Copy
byte[] b = ...String s1 = new String(b, "GBK"); // 按GBK转换String s2 = new String(b, StandardCharsets.UTF_8); // 按UTF-8转换StringBuilder#Copy
String s = "";for (int i = 0; i < 1000; i++) {    s = s + "," + i;}Copy
StringBuilder sb = new StringBuilder(1024);for (int i = 0; i < 1000; i++) {    sb.append(',');    sb.append(i);}String s = sb.toString();Copy
public class Main {    public static void main(String[] args) {        var sb = new StringBuilder(1024);        sb.append("Mr ")          .append("Bob")          .append("!")          .insert(0, "Hello, ");        System.out.println(sb.toString());    }}StringJoiner#Copy
public class Main {    public static void main(String[] args) {        String[] names = {"Bob", "Alice", "Grace"};        var sj = new StringJoiner(", ");        for (String name : names) {            sj.add(name);        }        System.out.println(sj.toString());    }}Copy
public class Main {    public static void main(String[] args) {        String[] names = {"Bob", "Alice", "Grace"};        // param1 是需要给数组之间插入的字符串 para2和3是指定了StringJoiner的开头和结尾        var sj = new StringJoiner(", ", "Hello ", "!");        for (String name : names) {            sj.add(name);        }        System.out.println(sj.toString());    }}String.join()#
String还提供了一个静态方法join(),这个方法在内部使用了StringJoiner来拼接字符串,在不需要指定“开头”和“结尾”的时候,用String.join()更方便:
Copy
String[] names = {"Bob", "Alice", "Grace"};var s = String.join(", ", names);包装类型#Copy
String s = null;int n = null; // compile error!Copy
Integer n = null;Integer n2 = new Integer(99);int n3 = n2.intValue();基本类型对应的引用类型
booleanjava.lang.Boolean
bytejava.lang.Byte
shortjava.lang.Short
intjava.lang.Integer
longjava.lang.Long
floatjava.lang.Float
doublejava.lang.Double
charjava.lang.CharacterCopy
public class Main {    public static void main(String[] args) {        int i = 100;        // 通过new操作符创建Integer实例(不推荐使用,会有编译警告):        Integer n1 = new Integer(i);        // 通过静态方法valueOf(int)创建Integer实例:        Integer n2 = Integer.valueOf(i);        // 通过静态方法valueOf(String)创建Integer实例:        Integer n3 = Integer.valueOf("100");        // 使用示范        System.out.println(n3.intValue());    }}Auto Boxing#Copy
Integer n = 100; // 编译器自动使用Integer.valueOf(int)int x = n; // 编译器自动使用Integer.intValue()
自动装箱和自动拆箱只发生在编译阶段,目的是为了少写代码。
不变类#Copy
public final class Integer {    private final int value;}
按照语义编程,而不是针对特定的底层实现去“优化”。
创建新对象时,优先选用静态工厂方法而不是new操作符。
进制转换#Copy
int x1 = Integer.parseInt("100"); // 100int x2 = Integer.parseInt("100", 16); // 256,因为按16进制解析Copy
public class Main {    public static void main(String[] args) {        System.out.println(Integer.toString(100)); // "100",表示为10进制        System.out.println(Integer.toString(100, 36)); // "2s",表示为36进制        System.out.println(Integer.toHexString(100)); // "64",表示为16进制        System.out.println(Integer.toOctalString(100)); // "144",表示为8进制        System.out.println(Integer.toBinaryString(100)); // "1100100",表示为2进制    }}JavaBean#Copy
public class Person {    private String name;    private int age;    public String getName() { return this.name; }    public void setName(String name) { this.name = name; }    public int getAge() { return this.age; }    public void setAge(int age) { this.age = age; }}Copy
// 读方法:public Type getXyz()// 写方法:public void setXyz(Type value)Copy
// 读方法:public boolean isChild()// 写方法:public void setChild(boolean value)JavaBean的作用#Copy
public class Person {    private String name;    private int age;}枚举JavaBean属性#枚举类#Copy
public class Weekday {    public static final int SUN = 0;    public static final int MON = 1;    public static final int TUE = 2;    public static final int WED = 3;    public static final int THU = 4;    public static final int FRI = 5;    public static final int SAT = 6;}Copy
if (weekday == 6 || weekday == 7) {    if (tasks == Weekday.MON) {        // TODO:    }}enum#Copy
public class Main {    public static void main(String[] args) {        Weekday day = Weekday.SUN;        if (day == Weekday.SAT || day == Weekday.SUN) {            System.out.println("Work at home!");        } else {            System.out.println("Work at office!");        }    }}enum Weekday {    SUN, MON, TUE, WED, THU, FRI, SAT;}Copy
Weekday x = Weekday.SUN; // ok!Weekday y = Color.RED; // Compile error: incompatible typesenum的比较#
前面讲解过引用类型的比较需要使用equals(),虽然enum定义的是一种枚举类型,但是却可以例外用==来比较。这是因为enum类型的每个常量在JVM中只有一个唯一实例,所以可以直接用==比较。
enum类型#
通过enum定义的枚举类,和其他的class有什么区别?
答案是没有任何区别。enum定义的类型就是class,只不过它有以下几个特点:
例如,我们定义的Color枚举类:
Copy
public enum Color {    RED, GREEN, BLUE;}
编译器编译出的class大概就像这样:
Copy
public final class Color extends Enum { // 继承自Enum,标记为final class    // 每个实例均为全局唯一:    public static final Color RED = new Color();    public static final Color GREEN = new Color();    public static final Color BLUE = new Color();    // private构造方法,确保外部无法调用new操作符:    private Color() {}}
所以,编译后的enum类和普通class并没有任何区别。但是我们自己无法按定义普通class那样来定义enum,必须使用enum关键字,这是Java语法规定的。
因为enum是一个class,每个枚举的值都是class实例,因此,这些实例有一些方法:
name()#
返回常量名,例如:
Copy
String s = Weekday.SUN.name(); // "SUN"ordinal()#
返回定义的常量的顺序,从0开始计数,例如:
Copy
int n = Weekday.MON.ordinal(); // 1
改变枚举常量定义的顺序就会导致ordinal()返回值发生变化。
如果不小心修改了枚举的顺序,编译器是无法检查出这种逻辑错误的。要编写健壮的代码,就不要依靠ordinal()的返回值。因为enum本身是class,所以我们可以定义private的构造方法,并且,给每个枚举常量添加字段:
Copy
public class Main {    public static void main(String[] args) {        Weekday day = Weekday.SUN;        if (day.dayValue == 6 || day.dayValue == 0) {            System.out.println("Work at home!");        } else {            System.out.println("Work at office!");        }    }}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;    }}
默认情况下,对枚举常量调用toString()会返回和name()一样的字符串。但是,toString()可以被覆写,而name()则不行。我们可以给Weekday添加toString()方法。
Copy
public class Main {    public static void main(String[] args) {        Weekday day = Weekday.SUN;        if (day.dayValue == 6 || day.dayValue == 0) {            System.out.println("Today is " + day + ". Work at home!");        } else {            System.out.println("Today is " + day + ". Work at office!");        }    }}enum Weekday {    MON(1, "星期一"), TUE(2, "星期二"), WED(3, "星期三"), THU(4, "星期四"), FRI(5, "星期五"), SAT(6, "星期六"), SUN(0, "星期日");    public final int dayValue;    private final String chinese;        private Weekday(int dayValue, String chinese) {        this.dayValue = dayValue;        this.chinese = chinese;    }        @Override    public String toString() {        return this.chinese;    }}
注意:判断枚举常量的名字,要始终使用name()方法,绝不能调用toString()!
switch#
因为枚举类天生具有类型信息和有限个枚举常量,所以比int、String类型更适合用在switch语句中。
BigInteger#Copy
BigInteger i1 = new BigInteger("1234567890");BigInteger i2 = new BigInteger("12345678901234567890");BigInteger sum = i1.add(i2); // 12345678902469135780BigInteger mul = i1.multiply(i2); //不知道多大了System.out.println(i.multiply(i).longValueExact()); // java.lang.ArithmeticException: BigInteger out of long range// 使用longValueExact()方法时,如果超出了long型的范围,会抛出ArithmeticExceptionBigDecimal#Copy
BigDecimal bd = new BigDecimal("123.4567");System.out.println(bd.multiply(bd)); // 15241.55677489Copy
BigDecimal d1 = new BigDecimal("123.45");BigDecimal d2 = new BigDecimal("123.4500");BigDecimal d3 = new BigDecimal("1234500");System.out.println(d1.scale()); // 2,两位小数System.out.println(d2.scale()); // 4System.out.println(d3.scale()); // 0Copy
BigDecimal d1 = new BigDecimal("123.4500");BigDecimal d2 = d1.stripTrailingZeros();System.out.println(d1.scale()); // 4System.out.println(d2.scale()); // 2,因为去掉了00BigDecimal d3 = new BigDecimal("1234500");BigDecimal d4 = d3.stripTrailingZeros();System.out.println(d3.scale()); // 0System.out.println(d4.scale()); // -2Copy
import java.math.BigDecimal;import java.math.RoundingMode;public class Main {    public static void main(String[] args) {        BigDecimal d1 = new BigDecimal("123.456789");        BigDecimal d2 = d1.setScale(4, RoundingMode.HALF_UP); // 四舍五入,123.4568        BigDecimal d3 = d1.setScale(4, RoundingMode.DOWN); // 直接截断,123.4567        System.out.println(d2);        System.out.println(d3);    }}Copy
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,因为除不尽Copy
import java.math.BigDecimal;public class Main {    public static void main(String[] args) {        BigDecimal n = new BigDecimal("12.345");        BigDecimal m = new BigDecimal("0.12");        BigDecimal[] dr = n.divideAndRemainder(m);        System.out.println(dr[0]); // 102        System.out.println(dr[1]); // 0.105    }}Copy
BigDecimal n = new BigDecimal("12.75");BigDecimal m = new BigDecimal("0.15");BigDecimal[] dr = n.divideAndRemainder(m);if (dr[1].signum() == 0) {    // n是m的整数倍}比较BigDecimal#Copy
BigDecimal d1 = new BigDecimal("123.456");BigDecimal d2 = new BigDecimal("123.45600");System.out.println(d1.equals(d2)); // false,因为scale不同System.out.println(d1.equals(d2.stripTrailingZeros())); // true,因为d2去除尾部0后scale变为2System.out.println(d1.compareTo(d2)); // 0Copy
public class BigDecimal extends Number implements Comparable<BigDecimal> {    private final BigInteger intVal;    private final int scale;}常用工具类#Math#
顾名思义,Math类就是用来进行数学计算的,它提供了大量的静态方法来便于我们实现数学计算:
求绝对值:
Copy
Math.abs(-100); // 100Math.abs(-7.8); // 7.8
取最大或最小值:
Copy
Math.max(100, 99); // 100Math.min(1.2, 2.3); // 1.2
计算xy次方:
Copy
Math.pow(2, 10); // 2的10次方=1024
计算√x:
Copy
Math.sqrt(2); // 1.414...
计算ex次方:
Copy
Math.exp(2); // 7.389...
计算以e为底的对数:
Copy
Math.log(4); // 1.386...
计算以10为底的对数:
Copy
Math.log10(100); // 2
三角函数:
Copy
Math.sin(3.14); // 0.00159...Math.cos(3.14); // -0.9999...Math.tan(3.14); // -0.0015...Math.asin(1.0); // 1.57079...Math.acos(1.0); // 0.0
Math还提供了几个数学常量:
Copy
double pi = Math.PI; // 3.14159...double e = Math.E; // 2.7182818...Math.sin(Math.PI / 6); // sin(π/6) = 0.5
生成一个随机数x,x的范围是0 <= x < 1:
Copy
Math.random(); // 0.53907... 每次都不一样
如果我们要生成一个区间在[MIN, MAX)的随机数,可以借助Math.random()实现,计算如下:
Copy
// 区间在[MIN, MAX)的随机数public class Main {    public static void main(String[] args) {        double x = Math.random(); // x的范围是[0,1)        double min = 10;        double max = 50;        double y = x * (max - min) + min; // y的范围是[10,50)        long n = (long) y; // n的范围是[10,50)的整数        System.out.println(y);        System.out.println(n);    }}
有些童鞋可能注意到Java标准库还提供了一个StrictMath,它提供了和Math几乎一模一样的方法。这两个类的区别在于,由于浮点数计算存在误差,不同的平台(例如x86和ARM)计算的结果可能不一致(指误差不同),因此,StrictMath保证所有平台计算结果都是完全相同的,而Math会尽量针对平台优化计算速度,所以,绝大多数情况下,使用Math就足够了。
Random#
Random用来创建伪随机数。所谓伪随机数,是指只要给定一个初始的种子,产生的随机数序列是完全一样的。
要生成一个随机数,可以使用nextInt()、nextLong()、nextFloat()、nextDouble():
Copy
Random r = new Random();r.nextInt(); // 2071575453,每次都不一样r.nextInt(10); // 5,生成一个[0,10)之间的intr.nextLong(); // 8811649292570369305,每次都不一样r.nextFloat(); // 0.54335...生成一个[0,1)之间的floatr.nextDouble(); // 0.3716...生成一个[0,1)之间的double
有童鞋问,每次运行程序,生成的随机数都是不同的,没看出伪随机数的特性来。
这是因为我们创建Random实例时,如果不给定种子,就使用系统当前时间戳作为种子,因此每次运行时,种子不同,得到的伪随机数序列就不同。
如果我们在创建Random实例时指定一个种子,就会得到完全确定的随机数序列:
Copy
import java.util.Random;public class Main {    public static void main(String[] args) {        Random r = new Random(12345);        for (int i = 0; i < 10; i++) {            System.out.println(r.nextInt(100));        }        // 51, 80, 41, 28, 55...    }}
前面我们使用的Math.random()实际上内部调用了Random类,所以它也是伪随机数,只是我们无法指定种子。
SecureRandom#
有伪随机数,就有真随机数。实际上真正的真随机数只能通过量子力学原理来获取,而我们想要的是一个不可预测的安全的随机数,SecureRandom就是用来创建安全的随机数的:
Copy
SecureRandom sr = new SecureRandom();System.out.println(sr.nextInt(100));
SecureRandom无法指定种子,它使用RNG(random number generator)算法。JDK的SecureRandom实际上有多种不同的底层实现,有的使用安全随机种子加上伪随机数算法来产生安全的随机数,有的使用真正的随机数生成器。实际使用的时候,可以优先获取高强度的安全随机数生成器,如果没有提供,再使用普通等级的安全随机数生成器:
Copy
import java.util.Arrays;import java.security.SecureRandom;import java.security.NoSuchAlgorithmException;public class Main {    public static void main(String[] args) {        SecureRandom sr = null;        try {            sr = SecureRandom.getInstanceStrong(); // 获取高强度安全随机数生成器        } catch (NoSuchAlgorithmException e) {            sr = new SecureRandom(); // 获取普通的安全随机数生成器        }        byte[] buffer = new byte[16];        sr.nextBytes(buffer); // 用安全随机数填充buffer        System.out.println(Arrays.toString(buffer));    }}
SecureRandom的安全性是通过操作系统提供的安全的随机种子来生成随机数。这个种子是通过CPU的热噪声、读写磁盘的字节、网络流量等各种随机事件产生的“熵”。
在密码学中,安全的随机数非常重要。如果使用不安全的伪随机数,所有加密体系都将被攻破。因此,时刻牢记必须使用SecureRandom来产生安全的随机数。

作者: DawsonLee
出处:https://www.cnblogs.com/dawsonlee/p/12319787.html












欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2