Java学习之JavaSE-Java核心类库

第一章:常用类的概述和使用

一、常用的包(熟悉

(一)包的名称和功能

  • java.lang包 - 该包是Java语言的核心包,并且该包中的所有内容由Java虚拟机自动导入
    如:System类、String类、…
  • java.util包 - 该包是Java语言的工具包,里面提供了大量工具类以及集合类等。
    如:Scanner类、Random类、List集合、…
  • java.io包 - 该包是Java语言中的输入输出包,里面提供了大量读写文件相关的类等。
    如:FileInputStream类、FileOutputStream类、…
  • java.net包 - 该包是Java语言中的网络包,里面提供了大量网络编程相关的类等。
    如:ServerSocket类、Socket类、…
  • java.sql 包 - 该包是Java语言中的数据包,里面提供了大量操作数据库的类和接口等。
    如:DriverManager类、Connection接口、…
    … …
    Java程序员在编程时可以使用大量类库,因此Java编程时需要记的很多,对编程能力本身要求不是特别的高。

二、Object类的概述(重点

(一)基本概念

  • java.lang.Object类是Java语言中类层次结构的根类,也就是说任何一个类都是该类的直接或者间接子类。
  • 如果定义一个Java类没有使用extends关键字声明其父类时,则其父类为java.lang.Object 类
  • Object类定义了“对象”的基本行为, 被子类默认继承

(二)Object中的方法

1、registerNatives()方法
    private static native void registerNatives();static {registerNatives();}

一个本地方法,主要作用是将C/C++中的方法映射到Java中的native方法,实现方法命名的解耦。native 关键字是 JNI(Java Native Interface)的重要体现,native 关键字修饰的是方法,主要起声明作用,告诉 JVM 去调用这个方法,而 这个方法的实现在别的语言那已经实现了。在Object类中该方法的执行在静态代码块中,所以在类首次进行加载的时候就执行了该方法。

2、构造方法
    public Object() {}

使用无参方式构造对象。

3、getClass() 方法
    public final native Class<?> getClass();

getClass()方法被native修饰,表明是一个本地方法,通过JVM去调用,另外还被 final 修饰,所以该方法不能被子类重写,返回的 Class 对象是由所表示类的 static synchronized 方法锁定的对象,也就是返回此 Object 的运行时类

public class Test {public static void main(String[] args) {A test = new B();System.out.println(test.getClass());    // 返回运行时的类 class B}
}class A {}class B extends A {}
4、hashCode()方法
public native int hashCode();

这是一个本地方法,返回的是对象的哈希值,在编写hashCode()方法时,需要遵守以下规定:

  • (1)在 Java 应用程序执行期间,在对同一对象多次调用 hashCode ()方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
  • (2)如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode() 方法都必须生成相同的整数结果。 所以如果重写了equals()方法,一定也要重写hashCode()方法才可以
  • (3)如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode() 方法不要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
5、equals(Object obj)方法
    public boolean equals(Object obj) {return (this == obj);}

可以看到Object的equals(Object obj)方法最根本的实现就是‘==’,所以Object的equals(Object obj)方法比较的是地址,但是实际开发中一般根据对象的特有内容来判断对象是否相同,以此来覆盖该方法。
实体类:不同对象代表不同实体,采用Object类中的默认实现即可;
值类:关键域相等,就认为两个对象相等,所以需要重写equals方法。
equals方法在非空对象引用上实现了等价关系:
1)自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。
2)对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才返回 true。
3)传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 返回的也为true。
4)一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false的前提是对象上 equals 比较中所用的信息没有被修改。
5)非空性:对于任何非空引用值 x,x.equals(null) 都应返回 false。
(Java语法没有强制要求严格遵循以上这些原则,但是违反可能会对程序造成灾难性的后果)
Object 类的 equals 方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true(x == y 具有值 true)。
注意:当此方法被重写时,通常都有必要同时重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
拓展:"=="和 equals 的区别
“==”:
a)比较基本数据类型时,判断的是他们的值是否相等
b)比较引用数据类型时,判断的是他们是否是同一个对象
equals:
a)不能比较基本数据类型
b)比较引用数据类型时,如果没有重写Object的equals(),默认判断是否是同一个对象
c)如果重写了equals(),一般是根据对象的值进行判断

6、 clone()方法
protected native Object clone() throws CloneNotSupportedException;

这是一个本地方法,主要用于创建并返回调用对象的一个副本,需要注意该方法默认的是“浅拷贝”
所谓的“浅拷贝”,就比如一个对象①内部还有一个引用类型的基本变量,通过clone()方法拷贝该对象时,产生的新对象中同样拷贝了原对象的引用,也就是新对象①和原对象①的内部都含有一个指向同一个对象②的引用;而“深拷贝”则是会把对象①内部引用变量所引用的对象②复制一份,然后再让新对象①中产生的新引用去指向这个复制出来的的新对象②,也就是新对象①和原对象①的内部的引用指向的不再是同一个对象。
在这里插入图片描述

7、toString()方法
    public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}// getClass().getName()是获取字节码文件所对应的全路径名称,例如java.lang.Object// Integer.toHexString(hashCode())是将哈希值转成16进制数格式的字符串

该方法主要返回调用对象的字符串表示,该字符串由类名(对象是该类的一个实例)、at 标记符“@”和此对象哈希码的无符号十六进制表示组成。默认实现不能很好的体现对象,建议所有子类都重写此方法。

8、notify()方法
    public final native void notify();

本地方法,主要用来唤醒在此对象监视器上等待的单个线程,这种唤醒由JVM(与优先级无关)随机选择一个处于wait状态的线程。
这个方法只能被一个线程(线程需为对象的监视器的所有者)调用,而一个线程可以以下面3种方式成为这个对象的监视器的所有者:
a)在调用notify()之前,这个线程必须获得该对象的对象级别锁
b)执行完notify()方法后,不会马上释放锁,要直到退出synchronized代码块,当前线程才会释放锁
c)notify()一次只随机通知一个线程进行唤醒

9、notifyAll()方法
    public final native void notifyAll();

本地方法,主要用来唤醒在此对象监视器上等待的所有线程,但是在执行剩余的代码之前,所有被唤醒的线程都将争夺对象的锁,而优先级高的线程竞争到对象锁的概率大。
在java中,每个对象都有两个池,锁池和等待池,下面简单了解锁池和等待池的概念,以便更好的了解notify()与notifyAll()的区别。
锁池:当线程想要调用某对象的synchronized方法或synchronized块时,都必须先拿到这个对象的锁才可以进入synchronized方法或synchronized块中,假设线程A拿到了这个对象锁,那么其他线程将无权进入synchronized方法,而是进入到了这个对象的锁池中,等待线程A执行完synchronized方法将对象锁释放后,再一次争夺对象锁。
等待池:假设拿了对象锁的线程A调用了wait()方法,那么线程A将会释放对象锁并同时进入到该对象的等待池中,而在等待池中的线程将不再会去争夺该对象锁。
也就是说,调用了notify方法后,会从该对象的等待池中随机唤醒一个线程,然后将其移动到锁池中,而调用notifyAll方法则是将等待池中所有的线程都唤醒并移动到锁池中,然后等待锁的竞争,优先级越高的线程竞争到对象锁的概率越大,而没有竞争到对象锁的线程将会继续逗留在锁池中,等待竞争到对象锁的线程执行完synchronized方法后释放锁,然后继续竞争对象锁,只有线程再次调用wait()方法后,线程才能再次回到等待池中
以下是使用notify()和notifyAll()的示例:

public class NotifyAndNotifyAllTest {// 定义volatile变量,以便所有线程能够及时获取到最新值private volatile boolean flag;public static void main(String[] args) {// final修饰对象表示该对象的引用不能改变,但对象的内容是可以改变的final NotifyAndNotifyAllTest nant = new NotifyAndNotifyAllTest();// 由于接口不能实例化,使用匿名内部类的语法格式来获取接口类型的引用/*Runnable wait = new Runnable() {@Overridepublic void run() {try {nant.intoWaitPool();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "执行完成!");}};*/// 利用Lambda表达式的写法重写 run()    (参数列表) -> {方法体}Runnable waitOder = () -> {try {nant.intoWaitPool();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "执行完成!");};/*Runnable notifyOder = new Runnable() {@Overridepublic void run() {nant.intoLockPool();System.out.println(Thread.currentThread().getName() + "执行完成!");}};*/// Lambda表达式的写法Runnable notifyOder = () -> {nant.intoLockPool();System.out.println(Thread.currentThread().getName() + "执行完成!");};// 定义三个线程进入等待池中,一个线程进入到锁池中Thread t1 = new Thread(waitOder, "线程WT1:");Thread t2 = new Thread(waitOder, "线程WT2:");Thread t3 = new Thread(waitOder, "线程WT3:");Thread t4 = new Thread(notifyOder, "线程NT4:");// 先开启要进入到等待池中的线程t1.start();t2.start();t3.start();// 为确保要进入等待池的线程全部开启,线程休眠200毫秒后再开启要进入锁池的线程try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}// 最后开启要进入锁池的线程t4.start();}// 唤醒线程进入到锁池private synchronized void intoLockPool() {while (!flag) {System.out.println(Thread.currentThread().getName() + "准备调用notify()或notifyAll()唤醒线程");// 将标识置为 true,跳出此循环,同时让被唤醒的线程获取对象锁后可以跳出循环往下执行flag = true;// 调用notify()或notifyAll()唤醒线程//notify();notifyAll();}}// 线程进入等待池private synchronized void intoWaitPool() throws InterruptedException {while (!flag) {System.out.println(Thread.currentThread().getName() + "准备进入对象的等待池中");wait();System.out.println(Thread.currentThread().getName() + "被唤醒");}// 重置标识,第一个获取对象锁的线程将标识重置,其他线程将无法跳出循环,再次执行wait()方法重新回到等待池中flag = false;}
}
10、wait(long timeoutMillis)方法
public final native void wait(long timeoutMillis) throws InterruptedException;

本地方法,使进程进入等待状态,直到调用notify() 或 notifyAll()再将线程唤醒,又或者等到时间的限定值(时间单位为毫秒),另外调用了wait方法的当前线程一定要先拥有对象的监视器锁
调用wait方法后会把当前线程A放置在对应对象的等待池中,线程A将不再响应该对象的所有同步请求,JVM也将不再调度该线程A,直到以下情况发生前,该线程都将处于休眠状态:
1)当其他的线程在此对象上调用notify方法,在此对象的等待池中会随机唤醒一个线程;
2)其他的线程在此对象上调用了notifyAll方法;
3)其他的线程调用了interrupt方法来中断线程A;注意:如果在wait之前或者wait的时候,线程A被中断了,那么要直到该线程被恢复的时候才会抛出中断异常(InterruptedException)
4)等待的时间已经超过了wait中指定的时间。

11、wait()方法
    public final void wait() throws InterruptedException {wait(0L);}

本地方法,使进程进入等待状态,直到调用notify() 或 notifyAll()再将线程唤醒,可以看见wait()方法内部调用了()方法内部实际是调用了wait(timeoutMillis)的方法,0代表没有时间限制。

12、wait(long timeoutMillis, int nanos)方法
    public final void wait(long timeoutMillis, int nanos) throws InterruptedException {if (timeoutMillis < 0) {throw new IllegalArgumentException("timeoutMillis value is negative");}if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanosecond timeout value out of range");}if (nanos > 0) {timeoutMillis++;}wait(timeoutMillis);}

本地方法,使进程进入等待状态,直到调用notify() 或 notifyAll()再将线程唤醒,又或者等到限定的超时时间(超时时间 = 限定时间 + 附加时间),可以看见内部调用了wait(timeoutMillis)的方法(注意:timeoutMillis单位为毫秒,nanos单位为纳秒,1毫秒 = 1000 微秒 = 1000 000 纳秒)。

13、finalize()方法
    @Deprecated(since="9")protected void finalize() throws Throwable { }

finalize()是Object中的protected方法,主要用于实现垃圾回收,子类可以通过重写覆盖该方法来实现资源清理工作,GC(垃圾回收)在回收对象之前调用该方法。可以看到Object源码中,finalize()方法从java9后就已被弃用,原因是finalize()方法的调用具有不确定性,它只能保证该方法会调用,但不能保证方法里的任务会被执行。

三、包装类(熟悉

(一)概念

通常情况下基本数据类型的变量不是对象,为了满足万物皆对象的理念就需要对基本数据类型的变量进行打包封装处理变成对象,而负责将这些变量声明为成员变量进行对象化处理的相关类,叫做包装类
如:Person p = new Person(); int num = 10;
之所以引入包装类,是因为java语言是一门面向对象的语言,很多的类和方法中的参数都需使用对象(例如:集合),而基本数据类型不是面向对象的,这样会造成诸多不便,为了解决这类的问题,官方编写封装类来将基本数据类型进行封装,使其具有对象的属性与方法。
另外,包装类型都使用final声明,所以包装类都不可以被继承重写

(二)包装类的分类

在这里插入图片描述

1)Integer类
1、基本概念

java.lang.Integer类内部包装了一个int类型的变量作为成员变量,主要用于实现对int类型的包装并提供int类型到String类之间的转换等方法

2、常用的常量

在这里插入图片描述

3、常用的方法

在这里插入图片描述

4、装箱和拆箱的概念
  • 装箱:将基本数据类型转化为对应类型的包装类的过程。
  • 拆箱:将包装类转化为对应类型的基本数据类型的过程。
  • 在Java5发布之前使用包装类对象进行运算时,需要较为繁琐的“拆箱”和“装箱”操作;即运算前先将包装类对象拆分为基本类型数据,运算后再将结果封装成包装类对象。
  • 从Java5开始增加了自动拆箱和自动装箱的功能。
// 装箱
int num = 5;
Integer i = Integer.valueOf(num);
// 自动装箱
Integer i2 = num;// 拆箱
int a = i.intValue();
// 自动拆箱
int b = i2;
5、自动装箱池

在Integer类的内部提供了自动装箱池技术,将-128到127之间的整数已经装箱完毕,当程序中使用该范围之间的整数时,无需装箱直接取用自动装箱池中的对象即可,从而提高效率。

        Integer i = 10;Integer j = 10;System.out.println(i == j);         // ==比较的是对象的地址,由于Integer中设置了一个缓冲池,-128到127间的数据不会创建新的对象 -> trueSystem.out.println(i.equals(j));    // equals比较的是对象的内容 -> trueSystem.out.println("==================");Integer a = 128;Integer b = 128;System.out.println(a == b);         // 比较对象的值已超过缓冲池设置的范围,所以两个为不同的对象,地址也不同 -> falseSystem.out.println(a.equals(b));    // trueSystem.out.println("==================");Integer m = Integer.valueOf(10);Integer n = Integer.valueOf(10);System.out.println(m == n);         // valueOf方法的源码中是通过new关键字来创建对象返回,所以两个为不同的对象 -> falseSystem.out.println("==================");int num = 10;System.out.println(i == num);       // Integer先自动拆箱为基本数据类型后再进行比较,也就是最终为两个基本数据类型相比较 -> trueSystem.out.println(i.equals(num));  // trueSystem.out.println("==================");int num2 = 128;System.out.println(a == num2);      // 同上 -> trueSystem.out.println(a.equals(num2)); // true
2)Double类
1、基本概念

java.lang.Double类型内部包装了一个double类型的变量作为成员变量,主要用于实现对double类型的包装并提供double类型到String类之间的转换等方法

2、常用的常量

在这里插入图片描述

3、常用的方法

在这里插入图片描述
拓展:
java.lang.Number类是个抽象类,是上述类的父类来描述所有类共有的成员,Number类中提供了将包装类型拆箱成基本类型的方法。

3)Boolean类
1、基本概念

java.lang.Boolean类型内部包装了一个boolean类型的变量作为成员变量,主要用于实现对boolean类型的包装并提供boolean类型到String类之间的转换等方法

2、常用的常量

在这里插入图片描述

3、常用的方法

在这里插入图片描述
注意:

boolean b = Boolean.parseBoolean("1"); // 该方法的执行原理是:只要参数不为true或者TRUE时,则结果都是false,可以查看源码
4)Character类
1、基本概念

java.lang.Character类型内部包装了一个char类型的变量作为成员变量,主要用于实现对char类型的包装并提供字符类别的判断和转换等方法

2、常用的常量

在这里插入图片描述

3、常用的方法

在这里插入图片描述

(三)包装类(Wrapper)的使用总结

  • 基本数据类型转换为对应包装类的方式
    调用包装类的构造方法或静态方法即可
  • 获取包装类对象中基本数据类型变量数值的方式
    调用包装类中的xxxValue方法即可
  • 字符串转换为基本数据类型的方式
    调用包装类中的parseXxx方法即可

四、数学处理类(熟悉

(一)Math类

1)基本概念

java.lang.Math类主要用于提供执行数学运算的方法,如:对数,平方根。

2)常用的方法

在这里插入图片描述
注意:random()随机取值的范围是[0.0,1.0).

(二)BigDecimal类

1)基本概念

由于float类型和double类型在运算时可能会有误差,若希望**实现精确运算则借助java.math.BigDecimal类型加以描述(常用于涉及金钱方面)**。

2)常用的方法

在这里插入图片描述
拓展:

    public static void main(String[] args) {System.out.println(0.1 + 0.2);	// 0.30000000000000004// 精确运算BigDecimal bd1 = new BigDecimal("0.1");BigDecimal bd2 = new BigDecimal("0.2");System.out.println(bd1.add(bd2));	// 0.3// 注意事项BigDecimal bd3 = new BigDecimal("2");BigDecimal bd4 = new BigDecimal("0.3");//System.out.println(bd3.divide(bd4));  // 运行报错,java.lang.ArithmeticException// 因为用BigDecimal做除法的时候,在不整除的情况下,结果会是无限循环小数,从而导致异常产生// 所以BigDecimal做除法时需定义四舍五入的模式,也可以定义精确到小数点后几位// 方法一:public BigDecimal divide(BigDecimal divisor, RoundingMode roundingMode)// 方法二:public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode)// 常用的四舍五入模式有:// RoundingMode.UP:直接进一位,比如1.21,如果保留1位小数,得到的是1.3// RoundingMode.DOWN:直接省略多余的小数,比如1.28,如果保留1位小数,得到的是1.2// RoundingMode.HALF_UP:四舍五入,2.35保留1位,变成2.4// RoundingMode.HALF_DOWN:四舍五入,2.35保留1位,变成2.3// 注意:后边两种的区别就是如果保留的位数的后一位如果正好是5的时候,一个舍弃掉,一个进位;// 这一前提需要结果的总位数恰好比要保留的位数多一位,并且最后一位是恰好是5,才按照之前的规则进行运算,否则都是按照四舍五入运算BigDecimal bd5 = new BigDecimal("10");BigDecimal bd6 = new BigDecimal("8");System.out.println(bd5.divide(bd6,1,RoundingMode.HALF_UP));     // 1.3System.out.println(bd5.divide(bd6,1,RoundingMode.HALF_DOWN));   // 1.2BigDecimal bd7 = new BigDecimal("45");  // 45/7=6.4285714285...BigDecimal bd8 = new BigDecimal("7");System.out.println(bd7.divide(bd8,3,RoundingMode.HALF_UP));     // 6.429System.out.println(bd7.divide(bd8,3,RoundingMode.HALF_DOWN));   // 6.429}

(二)BigInteger类

1)基本概念

若希望表示比long类型范围还大的整数数据,则需要借java.math.BigInteger类型描述。

2)常用的方法

在这里插入图片描述

第二章:String类的概述和使用

一、String类的概念(重点

  • java.lang.String类用于描述字符串,Java程序中所有的字符串字面值都可以使用该类的对象加以描述,如:“abc”。
  • 该类由final关键字修饰,表示该类不能被继承
  • 从jdk1.9开始该类的底层不使用char[]来存储数据,而是改成 byte[]加上编码标记,从而节约了一些空间。
  • 该类描述的字符串内容是个常量不可更改,因此可以被共享使用
    如:
    String str1 = “abc”; // 其中"abc"这个字符串是个常量不可改变。
    str1 = “123”; // 将“123”字符串的地址赋值给变量str1。
    注意:改变str1的指向并没有改变指向的内容

二、常量池的概念

由于String类型描述的字符串内容是常量不可改变,因此Java虚拟机将首次出现的字符串放入常量池中,若后续代码中出现了相同字符串内容则直接使用池中已有的字符串对象而无需申请内存及创建对象,从而提高了性能。
所以 String str = new String(“abc”);可以认为是创建两个对象,也可能是一个对象,具体要看是不是首次出现的字符串,也就是常量池中是否存在"abc"这个字符串,若常量池中尚未存在,那么java虚拟机会先在常量池中new一个对应字符串的对象,然后再堆内存中也会new一个同样的对象。若常量池已存在"abc"字符串,那么虚拟机只会在堆内存中new一个对象,然后再将常量池中"abc"的地址指向刚刚new的对象。

三、常用的构造方法(熟记

在这里插入图片描述

四、常用的成员方法(熟记

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
拓展:

    public static void main(String[] args) {// 创建字符串对象的构造方式// 1、无参方式构造对象String str1 = new String();// 注意:无参方式构造的对象为空字符串对象(""),本质是对象只是没有内容而已,// 而 null 则表示空,连对象都没有,所以 null无法调用各种方法,否则会报空指针异常(NullPointerException)System.out.println("str1 = " + str1);System.out.println("--------------------");// 2、使用参数指定的byte数组来构造对象,思路为先将每个整数转换为对应的字符,再将所有的字符进行拼接byte[] bArr = {97, 98, 99, 100, 101};   // 'a'->97// 2.1、使用整个字节数组来构造对象String str2 = new String(bArr);System.out.println("str2 = " + str2);   // abcde// 2.2、使用字节数组中的其中一部分来构造对象String str3 = new String(bArr, 0, 3);System.out.println("str3 = " + str3);   // abcSystem.out.println("--------------------");// 3、使用字符数组来构造对象char[] cArr = {'h', 'e', 'l', 'l', 'o'};// 3.1、使用整个字符数组来构造对象String str4 = new String(cArr);System.out.println("str4 = " + str4);   // hello// 3.2、使用字符数组中的其中一部分来构造对象String str5 = new String(cArr, 0 ,2);System.out.println("str5 = " + str5);   // heSystem.out.println("--------------------");// 4、使用字符串构造对象String str6 = new String("abc");System.out.println("str6 = " + str6);   // abc}
        // 将字符串"123456"转换为整数的方法有:String str = new String("123456");// 方法一:使用Integer类中的parseInt()方法int num = Integer.parseInt(str);System.out.println(num);// 方法二:利用ASCII来实现转换int num2 = 0;for (int i = 0; i < str.length(); i++) {num2 = num2 * 10 + (str.charAt(i) - '0');   // 1->12->123->...}System.out.println(num2);System.out.println("----------------");// 实现将整数转换为字符串int i = 123;// 方法一String str2 = String.valueOf(i);// 方法二:(推荐使用)String str3 = i + "";

五、正则表达式的概念(了解

正则表达式本质就是一个“规则字符串”,可以用于对字符串数据的格式进行验证,以及匹配、查找、替换等操作。该字符串通常使用^运算符作为开头标志,使用$运算符作为结尾标志,当然也可以省略

六、正则表达式的规则(了解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

七、正则表达式相关的方法(熟悉

在这里插入图片描述
在这里插入图片描述

	public static void main(String[] args) {// 使用正则表达式貌似手机号的规则:由1开头,第二位数需为3、4、5、7、8中的一位,总共11位数字String reg = "1[34578]\\d{9}";Scanner sc = new Scanner(System.in);for (int i = 4; i >= 0; i--) {if (0 == i) {System.out.println("输入的手机号格式错误,权限已被锁定");} else {System.out.println("请输入手机号:");String str = sc.next();if (str.matches(reg)) {System.out.println("输入的手机号格式正确!");break;} else if (0 != (i-1)) {System.out.println("输入的手机号格式错误,还有" + (i - 1) + "次输入机会");}}}}

第三章:可变字符串类和日期相关类

一、可变字符串类(重点

(一)基本概念

  • 由于String类描述的字符串内容是个常量不可改变,因此在每次对String类型进行改变时都相当于重新生成一个新的String对象,然后再将引用指向新的String对象,所以当需要在Java代码中描述大量类似的字符串时,只能单独申请和存储,此时不仅效率低下,而且会造成内存空间的浪费。
  • 如:String str = “abc”;str = str + “123”;这两个字符串操作,实际上需要开辟三次内存空间,首先初始化String值为“abc”,此时第一次开辟内存,然后字符串“123”也是需要先在栈堆区中先开辟内存空间,最后得到结果“abc123”字符串也是需要开辟内存空间,所以这样操作对内存空间上极大浪费的。
  • 为了解决上述问题,可以使用java.lang.StringBuilder类和java.lang.StringBuffer类来描述字符序列可以改变的字符串。
  • StringBuffer类是从jdk1.0开始存在,属于线程安全的类,因此效率比较低
  • StringBuilder类是从jdk1.5开始存在,属于非线程安全的类效率比较高
  • 底层均采用byte数组来存放所有的字符内容。
  • 当字符串的长度超过了字符串对象的初始容量时,该字符串对象会自动扩容,默认扩容算法是:原始容量*2 + 2

(二)StringBuilder类常用的构造方法

在这里插入图片描述

(三)StringBuilder类常用的成员方法

在这里插入图片描述
注意:

  • 作为参数传递的话,方法内部String不会改变其值,StringBuffer和StringBuilder会改变其值
  • StringBuilder delete(int start, int end) 删除字符串的范围是“左闭右开”。
  • StringBuilder类的对象本身可以修改,其成员方法还有返回值的原因是为了能够连续调用方法。如:sb.append(“1”).append(“2”).reverse();
  • String、StringBuilder、StringBuffer之间效率比较:String < StringBuffer < StringBuilder

(四)返回值的设计

  • StringBuilder的很多方法的返回值均为StringBuilder类型。这些方法的返回语句均为:return this。
  • 由此可见,这些方法在对StringBuilder所封装的字符序列进行改变后又返回了该对象的引用。基于这样设计的目的在于可以连续调用
  • 所以和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不会产生新的对象
(五)String、StringBuffer和StringBuilder之间的区别
  • String为不可变字符序列,StringBuffer和StringBuilder均为可变字符序列;
  • 初始化时String可以空赋值,StringBuffer和StringBuilder不可以空赋值,需创建一个对象;
  • StringBuffer和StringBuilder之间的最大区别在于 StringBuilder 的方法不是线程安全的(即不能进行同步访问),所以StringBuffer线程安全,StringBuilder线程不安全,但也因此在执行速度上StringBuilder比StringBuffer更有优势,所以多数情况下建议使用 StringBuilder 类,只有应用程序要求线程安全的情况才必须使用StringBuffer类。
  • 在某些特别情况下,String对象的字符串拼接会被JVM解释成StringBuffer对象的拼接,这些时候String对象的执行速度并不比StringBuffer慢;
// 情况一:
String str1 = "This is" + " a " + "test!";
// 这种情况下,String对象执行的效率远超StringBuffer,这是由于此时str1会直接被JVM视为字符常量(常量优化机制):String str1 = "This is a test!"; 
// 所以指向的是堆区中的拘留字符串对象,然后将地址存放在变量str1中,此过程的执行不需要太多的时间。// 情况二:
String s1 = "This is";
String s2 = " a ";
String s3 = "test!";
String str2 = s1 + s2 + s3;
// 变量s1、s2、s3存放了三个不同的拘留字符串对象的地址,然后再堆区中创建了一个StringBuilder类型的对象,这个对象先以s1进行初始化,再通过append()方法将s2、s3进行合并,接着再根据toString()方法在堆区中创建一个新对象,最后使用str2存储这个新对象的地址,可以看出这种情况下需要创建多个对象,因此效率自然会比较低下,但String变量的“+”拼接操作比String常量的“+”拼接使用更为广泛。// 情景三
StringBuffer sbf = new StringBuffer("This is").append(" a ").append("test!");
// 只需创建一个StringBuffer类型的对象,然后通过append()方法不停地扩大自身的容量以及存储新字符串即可,无需在堆区中创建任何新的对象,所以StringBuffer对象的append()累和连接比String对象的累"+"连接更为高效。

小结:

  • a、操作少量数据时建议会用String;
  • b、需要在多线程字符串缓冲区下操作大量数据时使用StringBuffer;
  • c、其他单线程操作字符串缓冲区下操作大量数据 时更建议使用StringBuilder。
  • d、在编译阶段就能够确定的字符串常量,直接使用字符串常量的"+"连接操作效率最高;
  • e、一般情况下,StringBuffer对象的append效率都要高于String对象的累"+"连接操作;
  • f、不停的创建对象是导致程序性能低效的一个重要原因。
(六)关于字符串相等关系的问题
问题一:
// 情景一:
String a = new String("abc");
String b = new String("abc");
System.out.println(a == b);		// false// 情景二:
String c = "abc";
String d = "abc";
System.out.println(c == d);		// true

分析:
情景一中,变量a和b存储的是JVM在堆区中new出来的两个String对象的地址,虽然两个对象的值都是"abc",但"=="比较的是两个对象不同的堆地址,所以得到的结果为false。
而情景二中,变量c和d存储的也是地址,但存储的都是常量池中"abc"字符串所指向堆区中的唯一的拘留字符串的地址,所以情景二的到的结果为true。

问题二:
// 情景一:
String s1 = "ab";
String s2 = "cd";
String str = s1 + s2;
String str1 = "abcd";
System.out.println(str == str1);	// false// 情景二:
String s3 = "ab" + "cd";
System.out.println(s3 == str);	// true

分析:
情景一中,变量s1和s2存储的是堆中两个拘留字符串对象的地址,当执行s1+s2的指令时,JVM首先会先在堆区中创建一个StringBuilder类,然后这个StringBuilder类以s1所指向的拘留字符串对象进行初始化,接着再通过调用append方法对s2所指向的拘留字符串进行拼接,再调用toString()方法在堆区中创建一个String类型的对象,最后将生成的String对象的堆地址存储在变量str中,而变量str1存储的则是常量池中"abcd"所指向的拘留字符串对象的地址,所以str与str1的地址不同,注意这个过程后堆区中实际有五个字符串的对象,分别为:三个拘留字符串对象,一个StringBuilder类型的对象以及一个String类型的对象。
而情景二中,字符串"ab"+“cd"会直接在编译期就已经拼接成了常量字符串"abcd”,而相同常量字符串所指向的是同一个拘留字符串对象,所以变量s3与str的地址是相同的。

问题三:

String与StringBuffer的可变性,查看两个类的源码可知,两者都是使用byte[]来存储数据(jdk1.9后),但String类中的为常量(final)数组,只能被赋值一次,而StringBuffer类中的为普通数组,可以通过append()方法添加新的字符串。
容易进入的误区:String str = new String(“abc”); str = new String(“123”); 这不是改变了字符串str了吗?
分析:一般栈区与方法区中存储的都是对象的引用,而并非对象本身的内容,所谓的对象本身是指存储在堆区中的实例数据,而对象的引用则是在栈区中存储在堆区中存放对象本身的地址,所以str实际改变的不是字符串对象本身,而是改变对象的引用。

二、Java8之前的日期相关类

(一)System类

1)基本概念

Java.lang.System类中提供了一些有用的类字段和方法。

2)常用的方法

在这里插入图片描述

(二)Date类

1)基本概念

java.util.Date类主要用于描述特定的瞬间,也就是年月日时分秒,可以精确到毫秒。

2)常用的方法

在这里插入图片描述

(三)SimpleDateFormat类

1)基本概念

java.text.SimpleDateFormat类主要用于实现日期和文本之间的转换

2)常用的方法

在这里插入图片描述

(四)Calendar类

1)基本概念
  • java.util.Calender类主要用于描述特定的瞬间,取代Date类中的过时方法实现全球化。
  • 该类是个抽象类,因此不能实例化对象,其具体子类针对不同国家的日历系统,其中应用最广泛的是GregorianCalendar(格里高利历),对应世界上绝大多数国家/地区使用的标准日历系统。
2)常用的方法

在这里插入图片描述

3)多态的使用场合
  • 通过方法的参数传递形成多态
public static void draw(Shape s){s.show();
}
draw(new Rect(1, 2, 3, 4));
  • 在方法体中直接使用多态的语法格式
Account acc = new FixedAccount();
  • 通过方法的返回值类型形成多态
// Calendar是个抽象类不能创建对象,通过返回子类GregorianCalendar对象形成多态
Calender getInstance(){return new GregorianCalendar(zone, aLocale);
}

三、Java8中的日期相关类

(一)Java8日期类的由来

JDK 1.0中包含了一个java.util.Date类,但是它的大多数方法已经在JDK 1.1引入Calendar类之后被弃用了,而Calendar并不比Date好多少。它们面临的问题是:

  • Date类中的年份是从1900开始的,而月份都从0开始
  • 格式化只对Date类有用,对Calendar类则不能使用。
  • 非线程安全等。

(二)Java8日期类的概述

  • Java 8通过发布新的Date-Time API来进一步加强对 日期与时间的处理。
  • java.time包:该包日期/时间API的基础包。
  • java.time.chrono包:该包提供对不同日历系统的访问。
  • java.time.format包:该包能够格式化和解析日期时间对象。
  • java.time.temporal包:该包包含底层框架和扩展特性。
  • java.time.zone包:该包支持不同时区以及相关规则的类。

(三)LocalDate类

1)基本概念

java.time.LocalDate类主要用于描述年-月-日格式的日期信息,该类不表示时间和时区信息

2)常用的方法

在这里插入图片描述

(四)LocalTime类

1)基本概念

java.time.LocalTime 类主要用于描述时间信息,可以描述时分秒以及纳秒

2)常用的方法

在这里插入图片描述

(五)LocalDateTime类

1)基本概念
  • java.time.LocalDateTime类主要用于描述ISO-8601日历系统中默认时区的日期时间,如2007-12-03T10:15:30。
  • LocalDateTime类与String类型相似,具有不可变性,调用对象本身的数据内容不会改变,返回值相当于创建了一个新的对象。
LocalDateTime ldf = LocalDateTime.of(2008, 8, 8, 20, 8, 8);
LocalDateTime ldf2 = ldf.withYear(2018);
System.out.println("ldf2 = " + ldf2); // 2018-08-08T20:08:08
System.out.println("ldf = " + ldf); // 2008-08-08T20:08:08
2)常用的方法

在这里插入图片描述
在这里插入图片描述

(六)Instant类

1)基本概念

java.time.Instant类主要用于描述瞬间的时间点信息

2)常用的方法

在这里插入图片描述

(七)DateTimeFormatter类

1)基本概念

java.time.format.DateTimeFormatter类主要用于格式化和解析日期。

2)常用的方法

在这里插入图片描述

第四章:集合类库

一、概述(重点

(一)集合的由来

  • 当需要在Java程序中记录单个数据内容时,则声明一个变量
  • 当需要在Java程序中记录多个类型相同的数据内容时,声明一个一维数组
  • 当需要在Java程序中记录多个类型不同的数据内容时,则创建一个对象
  • 当需要在Java程序中记录多个类型相同的对象数据时,创建一个对象数组
  • 当需要在Java程序中记录多个类型不同的对象数据时,则准备一个集合


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部