完整笔记
程序在运行过程中出现不正常的情况,导致运行终止。
引用数据类型变量的值为null,使用变量调用属性或者方法时,出现NullPointerException。
@Test
public void testNullPointerException(){
// 空指针异常
String s = null;
System.out.println(s.toString());
}
@Test
public void testNullPointerException(){
// 数组角标越界异常
int[] arr = {1,2,3};
System.out.println(arr[3]);
}
一般发生在向下转型时。
@Test
public void testClassCaseException(){
Object o = new Date();
String s = (String)o;
}
一般发生在将字符串转为包装类时。
@Test
public void testNumberFormatException(){
String s = "abc";
int i = Integer.parseInt(s);
}
使用Scanner从键盘输入不匹配的类型时。
@Test
public void testInputMismatchException(){
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
}
算术异常:除0
编译时异常:编写代码后,在IDE中会自动标出来,不解决无法运行。
抛:程序在运行过程中出现异常,就会在异常代码处生成异常类的对象,并将此对象抛出,一旦发生异常,其后的代码就不再执行;
异常产生的方式:① 系统自动生成 ② 手动生成异常对象,并throw
抓:可以理解为异常的处理方式。① try-catch-finally ② throws
String getMessage()
和void printStackTrace()
方法,表示异常描述信息,开发中常使用printStackTrace()
方法; @Test
public void testInputMismatchException(){
try {
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
}catch (NumberFormatException e){
System.out.println(e.getMessage());
}catch (InputMismatchException e){
e.printStackTrace();
}catch(Exception e){
System.out.println(e.getMessage());
}finally {
// 一定会执行的代码
}
}
@Test
public void test2() {
FileInputStream fis = null;
try {
File file = new File("hello1.txt");//文件可能不存在,而出现异常
fis = new FileInputStream(file);
int data = fis.read();
while (data != -1) {
System.out.print((char) data);
data = fis.read();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
格式:访问修饰符 返回值类型 方法名(形参) throws 异常类1,异常类2,…{ }
1.如果父类中被重写的方法没有使用throws抛异常,而子类重写的方法有异常,那么只能在子类中使用try-catch-finally处理异常;
2.如果方法a在运行的过程中需要调用方法b,而方法b需要调用方法c,那么方法b、c要使用throws抛出异常,在方法a中使用try-catch-finally处理;
运行时异常,可以不用处理。
public void test1(int i) {
if (i < 0) {
throw new RuntimeException("输入不合规");
}
}
Exception in thread "main" java.lang.RuntimeException: 输入不合规
at Exception.ThrowsTest.test1(ThrowsTest.java:30)
at Exception.ThrowsTest.main(ThrowsTest.java:21)
public void test1(int i) throws Exception {
if (i < 0) {
throw new Exception("输入不合规");
}
}
要求:
1.继承于现有的异常结构:RuntimeException(运行时) 、Exception(运行时+编译时);
2.提供全局常量:serialVersionUID;
3.提供重载的构造器;
4.自定义的异常通过throw抛出;
public class MyException extends RuntimeException{
static final long serialVersionUID = 123123213;
public MyException() {
}
public MyException(String msg){
super(msg);
}
}
1.throws是声明异常,throw是抛出异常;
2.throws是处理异常的一种方式,而throw是手动抛出异常;
3.位置不同:throws在方法声明处,后面时异常类名,throw在方法体中,后面是异常对象;
4.throws表示可能会出现异常,而throw一定会抛出一个异常对象;
正确的:
if(p instanceof Architect){
if(t.getArchitectCount()==1)
throw new AddException("团队中至多只能有一名架构师");
}else if (p instanceof Designer){
if(t.getDesignerCount()==2)
throw new AddException("团队中至多只能有两名设计师");
}else if (p instanceof Programmer){
if(t.getProgrammerCount()==3)
throw new AddException("团队中至多只能有三名程序员");
}
错误的:
if (p instanceof Architect && t.getArchitectCount() == 1) {
throw new AddException("团队中至多只能有一名架构师");
} else if (p instanceof Designer && t.getDesignerCount() == 2) {
throw new AddException("团队中至多只能有两名设计师");
} else if (p instanceof Programmer && t.getProgrammerCount() == 3) {
throw new AddException("团队中至多只能有三名程序员");
}
在前端页面输出实体类的信息,可以利用继承和多态的特性,重写toString方法,如果其中有不同的信息,可以在父类中提取出一个公共的方法,让所有子类的toString方法调用这个方法,而不用调用父类中的toString方法,也能实现代码复用。输出时,所有父子类对象都只要调用toString即可输出对应的信息,大大降低了代码的冗余。
为了完成特定任务,使用某种语言编写的一组指令。
正在运行中的程序,是资源分配的最小单位。
生命周期:产生-运行-消亡
并行:多个CPU同时执行多个任务。
并发:一个CPU(采用时间片)同时执行多个任务。
java.lang.IllegalThreadStateException
异常;public class ThreadTest extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0)
System.out.println(i);
}
}
}
class Test {
public static void main(String[] args) {
ThreadTest t1 = new ThreadTest();
t1.start();
for (int i = 0; i < 100; i++) {
System.out.println("-------------------------");
}
}
}
new Thread() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0)
System.out.println(Thread.currentThread().getName() + "," + i);
}
}
}.start();
class RunnableTest implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0)
System.out.println(Thread.currentThread().getName() + i);
}
}
}
class Test {
public static void main(String[] args) {
RunnableTest r = new RunnableTest();
Thread t = new Thread(r, "线程1");
t.start();
}
}
new Thread(new Runnable(){
@Override
public void run() {
System.out.println(1);
}
},"线程1").start();
因为不是继承的Thread类,所以调用Thread中的方法时,需要使用类名或者对象。
线程在被调度时执行的操作
表示当前正在执行中的线程
获取当前线程的名称
设置当前线程的名称
在线程a中调用线程b的join方法,会阻塞线程a,直到线程b执行完毕为止(在另一个线程执行完毕前都不会执行本线程)
使当前线程进入休眠状态,time以毫秒为单位
判断当前线程是否还存活
MAX_PRIORITY:最高优先级(10)
MIN_PRIORITY:最低优先级(1)
NORM_PRIORITY:普通优先级(5)
线程的优先级默认为5。
线程对象.setPriority(Thread.MAX_PRIORITY);设置线程的优先级
线程对象.setPriority(8); 设置线程的优先级
线程对象.getPriority(); 获取线程的优先级
优先级高的线程只是被cpu执行的概率更高,而并非一定在低优先级的线程之前执行;
一般优先使用实现Runnable接口的方式
多个线程共享一个数据时,一个线程操作共享数据时被另一个线程抢占了CPU,从而造成了数据不同步的问题。
共享数据:多个线程共同操作的属性。
优点:解决了线程安全的问题。
缺点:执行同步代码时,相当于是单线程,降低了程序运行的速度。
同步:某个时间段内只能执行一件事,加锁实现同步。
异步:某个时间段内多件事同时执行。当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。
同步监视器:可以是任意对象,但使用同一资源的所有线程必须使用同一对象。
操作共享数据的代码不能包含的太多,也不能包含的太少。
synchronized(同步监视器){
操作共享数据的代码
}
对于同步监视器,可以使用当前对象this,因为所有线程都是使用的同一个对象。
class PocketTest1 implements Runnable {
private int count = 100;
@Override
public void run() {
while (true) {
synchronized(this){
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "卖出1张,还有" + count + "张");
count--;
}else
break;
}
}
}
}
对于同步监视器,不能使用this,可以使用静态对象,也可以使用当前类(类名.class)。
class PocketTest extends Thread {
private static int count = 100;
public static Object o = new Object();
@Override
public void run() {
while (true) {
synchronized(o){
synchronized(PocketTest.class){
if (count > 0) {
System.out.println(getName() + "卖出1张,还有" + --count + "张");
} else
break;
}
}
}
public PocketTest(String name) {
super(name);
}
}
同步方法中也有同步监视器,只是不用显示声明。
非静态方法的同步监视器是this,静态方法的同步监视器是类(类名.class)
public synchronized static SingleTon getSingleTon() {
if (singleTon == null) {
singleTon = new SingleTon();
}
return singleTon;
}
非静态方法的同步监视器是this,静态方法的同步监视器是类
class PocketTest1 implements Runnable {
private int count = 1000;
@Override
public void run() {
while (true) {
test();
}
}
public synchronized void test(){
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "卖出1张,还有" + count + "张");
count--;
}
}
}
必须将同步方法声明为static,本类对象才能共用一个同步监视器
class PocketTest extends Thread {
private static int count = 1000;
@Override
public void run() {
while (true) {
test();
}
}
public synchronized static void test(){
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "卖出1张,还有" + count + "张");
count--;
}
}
public PocketTest(String name) {
super(name);
}
}
方式1:同步方法
public class SingleTon {
private SingleTon() {
}
private static SingleTon singleTon;
public synchronized static SingleTon getSingleTon() {
if (singleTon == null) {
singleTon = new SingleTon();
}
return singleTon;
}
}
方式2:同步代码块
public class SingleTon {
private SingleTon() {
}
private static SingleTon singleTon;
public static SingleTon getSingleTon(){
synchronized (SingleTon.class){
if (singleTon == null) {
singleTon = new SingleTon();
}
return singleTon;
}
}
}
方式3:效率更高的同步代码块
public class SingleTon {
private SingleTon() {
}
private static SingleTon singleTon;
public static SingleTon getSingleTon(){
if (singleTon == null) {
synchronized (SingleTon.class){
if (singleTon == null) {
singleTon = new SingleTon();
}
}
}
return singleTon;
}
}
不同线程占用对方所需的资源而不放弃,都在等待对方放弃自己所需的资源,从而形成了死锁。
class PocketTest1 implements Runnable {
private int count = 100;
// 1.创建ReentrantLock对象
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
// 2.上锁
lock.lock();
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "卖出1张,还有" + --count + "张");
}else{
break;
}
} finally {
// 3.解锁
lock.unlock();
}
}
}
}
相同点:都可以解决线程安全问题
不同点:synchronized执行完相应的同步代码后,自动释放同步监视器;Lock需要手动上锁和解锁。
wait():阻塞当前线程并且释放同步监视器;
notify():唤醒一个被wait()的线程,如果有多个线程,具体唤醒顺序由jvm决定 详解;
notifyAll():唤醒所有被wait()的线程;
相同点:都可以使线程进入阻塞状态;
不同点:1.在同步代码块或者同步方法中调用sleep()不会释放同步监视器,而调用wait()会释放同步监视器;
2.声明位置不同:sleep是Thread中的静态方法,wait是Object中的方法;
3.调用位置不同:sleep可以在多线程的任意场景中调用,wait只能在同步代码块或同步方法中调用;
/**
* @Description: 生产者消费者问题
* @author:zhou
* @create: 2022-02-04 15:54
*/
class ProduceTest{
public static void main(String[] args) {
Clerk clerk = new Clerk();
new Thread(new Producer(clerk),"生产者1").start();
new Thread(new Customer(clerk),"消费者1").start();
new Thread(new Customer(clerk),"消费者2").start();
}
}
public class Producer implements Runnable {
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.product();
}
}
}
class Customer implements Runnable {
private Clerk clerk;
public Customer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.custome();
}
}
}
class Clerk {
private int count = 0;
public void product() {
synchronized (this) {
if (count < 20) {
count++;
System.out.println(Thread.currentThread().getName() + "生产了第" + count + "件产品");
notify();
} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void custome() {
synchronized (this) {
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "消费了第" + count + "件产品");
count--;
notify();
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class CallableTest implements Callable {
// 重写call()方法
@Override
public Object call() throws Exception {
System.out.println(":12312");
return null;
}
}
class Test3{
public static void main(String[] args) {
// 创建Callable实现类对象
CallableTest callableTest = new CallableTest();
// 创建FutureTask对象
FutureTask future = new FutureTask(callableTest);
// 创建并启动线程
new Thread(future,"Thread-1").start();
try {
// 获取call()方法的返回值
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。(开发中优先使用这种方法)
public class ThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1. 创建线程池对象
ExecutorService executorService = Executors.newFixedThreadPool(10);
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorService;
// 设置线程池的属性
threadPoolExecutor.setCorePoolSize(5);
threadPoolExecutor.setMaximumPoolSize(10);
// 2. 创建线程任务
RunnableTest1 runnableTest1 = new RunnableTest1();
CallableTest1 callableTest = new CallableTest1();
// 3.提交并执行任务
// submit适用于实现Runnable接口和实现Callable接口的线程
// 返回Future对象,可以调用get()方法的到call()方法的返回值
Future submit = executorService.submit(callableTest);
System.out.println(submit.get());
executorService.submit(runnableTest1);
// execute只适用于实现Runnable接口的线程
executorService.execute(runnableTest1);
// 4.关闭线程池
executorService.shutdown();
}
}
class RunnableTest1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
class CallableTest1 implements Callable {
@Override
public Object call() throws Exception {
for (int i = 100; i > 0; i--) {
System.out.println(i);
}
return null;
}
}
需要多个任务同时执行的时候。例如我方坦克需要键盘控制移动,而发射的子弹需要按一定速度移动,要求两者可以同时移动,所以需要创建一个线程用于子弹位置的计算。
当不满足条件时,关闭线程。例如子弹位置超出画布边界或者敌方坦克被击毁。
为了显示动态效果,一般在操作后需要使用sleep方法休眠一段时间,以便将当前的状态绘制出来。
字面量直接创建:String s1 = "abc";
通过new创建:String s2 = new String("abc");
对象[index]
的形式获取; String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
String s4 = new String("abc");
System.out.println(s1==s2); // true
System.out.println(s1==s3); // false
System.out.println(s4==s3); // false
通过charAt方法获取str的各位字符
String str = "abcd";
for (int i = 0; i < str.length(); i++) {
System.out.println(str.charAt(i));
}
String s1 = "hello";
String s2 = "world";
String s3 = "helloworld";
String s4 = "hello" + "world";
String s5 = new String("hello") + "world";
String s6 = s1 + "world";
String s7 = s1 + s2;
System.out.println(s3 == s4); //true
System.out.println(s3 == s5); //false
System.out.println(s3 == s6); //false
System.out.println(s3 == s7); //false
System.out.println(s3 == s7.intern()); //true
System.out.println(s3 == s5.intern()); //true
s1 += "world";
System.out.println(s1 == s3); //false
System.out.println(s1.intern() == s3); //true
final String s8 = "hello";
String s9 = s8 + "world";
System.out.println(s9 == s3); //true
以下创建对象的个数都是基于常量池不存在创建的字符串情况下:
String s1 = "a";
创建了1个对象s1 = s1 + "b";
创建了两个对象:堆中和常量池各一个,相当于s1 = new String("ab")
String s2 = "ab";
创建了1个对象String s3 = "a" + "b";
创建了1个对象,等价于String s2 = "ab";
String s4 = s1.intern();
虽然String是引用数据类型,参数传递的是地址值,但是由于字符串的不可变性,会在内存中重新开辟空间存放"test ok"并赋值给局部变量str,而属性str的值并没有改变;
字符数组也是引用数据类型,但是其内容是可变的,所以字符数组的值会改变。
public class StringTest {
String str = new String("good");
char[] ch = { 't', 'e', 's', 't' };
public void change(String str, char ch[]) {
str = "test ok";
ch[0] = 'b';
}
public static void main(String[] args) {
StringTest ex = new StringTest();
ex.change(ex.str, ex.ch);
System.out.print(ex.str); //good
System.out.println(ex.ch); //best
} }
去除字符串的首尾空格,中间的不管。
比较两个字符串的大小:逐位比较,如果对应位置的字符不同,返回字符的差值;如果对应位置字符都相同,返回字符串长度的差值。
String s1 = "abcddsfaf";
String s2 = "abc";
System.out.println(s1.compareTo(s2)); // 6
String s3 = "dbcdds";
String s4 = "abcaa";
System.out.println(s3.compareTo(s4)); // 3
从指定索引处开始截取字符串直至末尾。
此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
判断此字符串从给定的索引处toffset开始的子字符串是否以前缀prifix开始。
String s3 = "dbcdds";
System.out.println(s3.startsWith("db")); // true
System.out.println(s3.startsWith("db",1)); // false
判断此字符串中是否包含子串s,s可以传入字符串。
严格区分大小写。
String s3 = "dbcdds";
System.out.println(s3.contains("bcd")); //true
System.out.println(s3.contains("bcD"));//false
返回str在此字符串中第一次出现的索引,没有返回-1。
返回str在此字符串指定索引开始的子串中第一次出现的索引,没有返回-1。
String s3 = "dbcdds";
System.out.println(s3.indexOf("d",4)); // 4
判断题:方法IndexOf((char ch,-1)返回字符ch在字符串中最后一次出现的位置。❌
IndexOf只会从前向后查找,不会反向查找。
返回str在此字符串中最后一次出现的索引,没有返回-1。
什么时候indexOf和lastIndexOf返回值相同?
str在字符串中出现了1次或0次。
返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索,没有返回-1。
String s3 = "dbcdds";
System.out.println(s3.lastIndexOf("d",2)); // 0
字符串的替换:可以替换单个字符,也可以替换字符串。
使 用 给 定 的replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
将所有的数字变成逗号,然后去除首位的逗号
String str = "12hello34world5java7891mysql456";
为什么需要\\,因为只有一个\是把d给转义了,在前面加一个\表示将第二个\转义成一个普通的字符,所以说\\d表示的就是普通字符\d。
System.out.println(str.replaceAll("\\d+", ",").replaceAll("^,|,$", ""));//hello,world,java,mysql
正则表达式中"."表示所有的字符,而"\."表示英文句号,而\在java中表示转义,所以需要用\\将\进行转义。
String str = ".12.hello34world5java7891mysql456.";
System.out.println(str.replaceAll("\\.", ",")); //,12,hello34world5java7891mysql456,
使 用 给 定 的replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
告知此字符串是否匹配给定的正则表达式。
根据给定正则表达式的匹配拆分此字符串。
String str = "hello|world|java";
String[] str1 = str.split("\\|");
for (int i = 0; i < str1.length; i++) {
System.out.println(str1[i]);
}
hello
world
java
根据匹配给定的正则表达式来拆分此字符串,limit影响生成的数组长度,该模式最多应用limit-1次
String str = "hello|world|java";
String[] str1 = str.split("\\|",2);
for (int i = 0; i < str1.length; i++) {
System.out.println(str1[i]);
}
hello
world|java
调用String的toCharArray()
String s2 = "abc123";
char[] chars1 = s2.toCharArray();
使用String的构造器
char[] chars3 = {'a','b','c'};
String str = new String(chars3);
System.out.println(str); // abc
编码:String转为byte[], 调用String的getBytes() 。(看得懂 -> 看不懂)
解码:byte[]转为String,使用String的构造器。(看不懂 -> 看得懂)
注意:编码解码使用的字符集要一致,不然会发生乱码。
public void testByte() throws UnsupportedEncodingException {
String s1 = "abcdef中国";
// 使用getBytes()无参方法,将使用编辑器的字符集进行编码
byte[] bytes = s1.getBytes();
// 指定字符集进行编码,解码时也要使用此字符集,不然会产生乱码
// 存在异常,需要处理(try或者throws)
byte[] gbks = s1.getBytes("gbk");
String s2 = new String(bytes);
System.out.println(s2); // abcdef中国 编码解码字符集一致,不会乱码
String s3 = new String(gbks);
System.out.println(s3); // abcdef�й� 编码解码字符集不一致,发生乱码
String s4 = new String(gbks,"gbk");
System.out.println(s4); // abcdef中国 编码解码字符集一致,不会乱码
}
将一个字符串进行反转。将字符串中指定部分进行反转。比如“abcdefg”反转为”abfedcg”
双指针同步移动
public void testReverse() {
String str = "abcdefg";
char[] chars = str.toCharArray();
int start = 0;
int end = 6;
for (int i = start, j = end; i < j; i++, j--) {
char temp = chars[i];
chars[i] = chars[j];
chars[j] = temp;
}
System.out.println(chars);
}
获取一个字符串在另一个字符串中出现的次数。
比如:获取“ ab”在 “abkkcadkabkebfkabkskab” 中出现的次数
public void testIndexOf() {
String str = "abcabdabeabfbab";
int count = 0;
int index = 0;
// index是从找到的ab索引位置+2
while ((index = str.indexOf("ab", index)) != -1) {
index += 2;
count++;
}
System.out.println(count);
}
获取两个字符串中最大相同子串。比如:
str1 = "abcwerthelloyuiodef“;str2 = “cvhellobnm”
提示:将短的那个串进行长度依次递减的子串与较长的串比较。
public String findSame(String str1, String str2) {
if (str1 == null || str2 == null)
return null;
if (str1.length() == 0 || str2.length() == 0)
return "";
String minStr = str1.length() >= str2.length() ? str2 : str1;
String maxStr = str1.length() < str2.length() ? str2 : str1;
for (int i = 0; i < minStr.length(); i++) {
for (int start = 0, end = minStr.length() - i; end <= minStr.length(); start++, end++) {
String str = minStr.substring(start,end);
if(maxStr.contains(str)){
return str;
}
}
}
return "";
}
public String[] findSame1(String str1, String str2) {
if (str1 == null || str2 == null)
return null;
if (str1.length() == 0 || str2.length() == 0)
return new String[]{};
String minStr = str1.length() >= str2.length() ? str2 : str1;
String maxStr = str1.length() < str2.length() ? str2 : str1;
StringBuilder s = new StringBuilder();
// 每一轮减少一个字母,内层循环判断所有的组合是否符合题意
for (int i = 0; i < minStr.length(); i++) {
for (int start = 0, end = minStr.length() - i; end <= minStr.length(); start++, end++) {
String str = minStr.substring(start, end);
if (maxStr.contains(str)) {
s.append(str + ",");
}
}
// 如果存在多个长度相同的最大相同子串,内层for循环可以全部找出来,找出来之后就不用再进行下一轮外层循环了
if (s.length() > 0)
break;
}
String[] split = s.delete(s.length() - 1, s.length()).toString().split(",");
return split;
}
底层都是使用字符数组存储
final char value[];
char[] value;
char[] value;
可变字符序列:修改字符串的内容后不会产生新的对象;
不可变字符序列:修改字符串的内容后会产生新的对象;
StringBuilder(线程不安全)>StringBuffer(线程安全)>String
// 方式1:底层创建了长度为16的char[]
// char[] value = new char[16];
StringBuffer stringBuffer = new StringBuffer();
System.out.println(stringBuffer.length());
stringBuffer.append('a'); // value[0] = 'a'
stringBuffer.append('b'); // value[1] = 'b'
// 方式2 底层:char[] value = new char["abc".length + 16];
StringBuffer stringBuffer1 = new StringBuffer("abc");
System.out.println(stringBuffer1.length());
// 方式3:char[] value = new char[30];
StringBuffer stringBuffer2 = new StringBuffer(30);
如果数组容量满了,会创建一个新的数组,长度一般为原来数组长度的2倍+2,并且将原来数组中的内容复制到新数组中。
开发中如果需要频繁对字符串进行添加、修改,那么建议使用StringBuffer(int capacity)和StringBuilder(int capacity),在创建对象时指定容量,避免后面扩容导致效率低下。
方法链的原理:这些方法虽然都有返回值,但是返回值都是当前对象本身,也就意味着调用这些方法会改变当前对象。
* StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接
* StringBuffer delete(int start,int end):删除指定位置的内容
* StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str
* StringBuffer insert(int offset, xxx):在指定位置插入xxx
* StringBuffer reverse() :把当前字符序列逆转
* public int indexOf(String str)
* public String substring(int start,int end):返回一个从start开始到end索引结束的左闭右开区间的子字符串
* public int length()
* public char charAt(int n )
* public void setCharAt(int n ,char ch)
StringBuffer stringBuffer2 = new StringBuffer(30);
stringBuffer2.append(-1); // 添加元素
stringBuffer2.append(2);
stringBuffer2.append(3);
stringBuffer2.append(4);
System.out.println(stringBuffer2);
stringBuffer2.delete(2,4); // 删除[2,4)之间的元素
stringBuffer2.replace(2,4,"hello"); // 将[2,4)之间的元素替换成hello
stringBuffer2.reverse(); // 反转字符串
stringBuffer2.insert(2,"c"); // 在索引为2的位置插入c,其他元素向后移
System.out.println(stringBuffer2);
stringBuffer2.setCharAt(1,'a'); // 只能替换指定索引处的单个字符
String str = null;
StringBuffer sb = new StringBuffer();
sb.append(str);
System.out.println(sb.length());// 4
System.out.println(sb);// null
StringBuffer sb1 = new StringBuffer(str);
System.out.println(sb1);// java.lang.NullPointerException
System类提供的public static long currentTimeMillis()用来返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。
时间戳可以用来充当订单号。
两种构造器:
new Date(); 使用无参构造器创建的对象可以获取本地当前时间。
new Date(long l);自定义毫秒时刻。
方法:
toString():输出具体的年月日时分秒
getTime():返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数(无参构造器表示到本地当前时间为止的毫秒数,有参构造器表示传入的参数)
构造器:
new Date(long l); 自定义毫秒时刻。
方法:
toString(): 输出年月日
方法1:多态,向下转型
Date d = new java.sql.Date(1231221321l);
java.sql.Date date2 = (java.sql.Date)d;
方法2:构造器传入
Date date = new Date(1234211545142l);
java.sql.Date date1 = new java.sql.Date(date.getTime());
因为Date类的API不易于国际化,大部分被废弃了,所以使用SimpleDateFormat对Date类进行格式化和解析。
格式化: Date -> String public String format(Date date)
解析:String -> Date public Date parse(String source)
空参输出默认格式
Date date3 = new Date();
SimpleDateFormat sdf = new SimpleDateFormat();
// 格式化:Date -> String
String format = sdf.format(date3);
System.out.println(format); //22-2-7 下午12:46
// 解析:String -> Date
try {
Date d = sdf.parse("22-2-7 下午12:42");
System.out.println(d); // Mon Feb 07 12:42:00 CST 2022
} catch (ParseException e) {
e.printStackTrace();
}
开发中一般使用这种方式,指定格式化和解析的格式。对字符串进行解析时,参数格式和字符串格式一定要相匹配。
y | 年 |
---|---|
M | 月 |
d | 日 |
H | 时 |
m | 分 |
s | 秒 |
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format1 = sdf1.format(date3);
System.out.println(format1); // 2022-02-07 12:46:51
try {
Date d = sdf1.parse(format1);
System.out.println(d); // Mon Feb 07 12:46:51 CST 2022
} catch (ParseException e) {
e.printStackTrace();
}
方式1:
String str = "2021-09-08";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 参数的格式要与解析的字符串格式相同
try {
Date d = sdf.parse(str);
java.sql.Date date = new java.sql.Date(d.getTime());
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
}
方式2:
String str = "2018-01-10";
java.sql.Date date = java.sql.Date.valueOf(str);
System.out.println(date);
public void test123() {
String date1 = "1990-01-01";
Date d1 = getDate(date1); // 把字符串转为Date
Date d2 = new Date(); // 获取当前日期
long l = d2.getTime() - d1.getTime();
long l1 = l / (24 * 60 * 60 * 1000) + 1;
System.out.println(l1);
}
public Date getDate(String str) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d = null;
try {
d = sdf.parse(str);
} catch (ParseException e) {
e.printStackTrace();
}
return d;
}
Calendar对象具有可变性,具体表现在可以通过set方法直接更改自身的值
方式1:调用静态方法getInstance(); Calendar instance = Calendar.getInstance();
方式2:通过子类GregorianCalendar构造器创建对象;
用于获取年份、月份、星期、某年的第几天、某星期的第几天等等。
将当前Calendar 对象中的字段设置为给定值。
Calendar instance = Calendar.getInstance();
System.out.println(instance.get(Calendar.YEAR)); // 2022
instance.set(Calendar.YEAR,2011);
System.out.println(instance.get(Calendar.YEAR)); // 2011
在当前基础上进行偏移
instance.add(Calendar.YEAR,10); // +10年
System.out.println(instance.get(Calendar.YEAR)); // 2032
instance.add(Calendar.YEAR,-10); // -10年
System.out.println(instance.get(Calendar.YEAR)); // 2022
将Calendar对象转为Date对象
Date time = instance.getTime();
System.out.println(time);
将Date对象转为Calendar对象,可以用于获取Date中的年月日
Date d = new Date();
instance.setTime(d);
System.out.println(instance.get(Calendar.YEAR)); // 2022
LocalDate:本地日期
LocalTime:本地时间
LocalDateTime:本地日期时间(使用频率最高)
静态方法,根据当地时间创建对象。
LocalDateTime now = LocalDateTime.now();
System.out.println(now); // 2022-02-07T16:17:33.847
静态方法,根据指定日期/时间创建对象
LocalDateTime of = LocalDateTime.of(2010, 10, 31, 10, 10, 10);
System.out.println(of); // 2010-10-31T10:10:10
获取年月日时分秒
System.out.println(now.getDayOfMonth()); // 7 获得月份天数(1-31)
System.out.println(now.getDayOfYear()); // 38 获得年份天数(1-366)
System.out.println(now.getDayOfWeek()); // MONDAY 获得星期几(返回一个 DayOfWeek 枚举值)
System.out.println(now.getMonth()); // FEBRUARY 获取月份
System.out.println(now.getMonthValue()); // 2 获取本年的第几个月
System.out.println(now.getHour()); //获得当前对象对应的小时
System.out.println(now.getMinute()); // 分钟
System.out.println(now.getSecond()); // 秒
设置年月日时分秒,是不可变的,即调用方法后会返回一个新的变量,原来的值不会改变。
withDayOfMonth()/withDayOfYear()/withMonth()/withYear()
LocalDateTime now = LocalDateTime.now();
LocalDateTime localDateTime = now.withDayOfMonth(28);
System.out.println(localDateTime.getDayOfMonth()); // 28
System.out.println(now.getDayOfMonth()); // 7
向当前对象添加几天、几周、几个月、几年、几小时,是不可变的。
plusDays(), plusWeeks(), plusMonths(), plusYears(),plusHours()
LocalDateTime localDateTime1 = now.plusDays(5);
System.out.println(localDateTime1.getDayOfMonth());
向当前对象减少几天、几周、几个月、几年、几小时,是不可变的。
LocalDateTime localDateTime2 = now.minusDays(6);
System.out.println(localDateTime2.getDayOfMonth());
与java.lang.Date类似,用于获取毫秒数或通过毫秒数获取对象
// now:获取本初子午线的时间
Instant i = Instant.now();
System.out.println(i);
// atOffset 添加时间偏移量,获取本地时间
OffsetDateTime offsetDateTime = i.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime);
// toEpochMilli 获取时间戳,相当于Date类的getTime()方法
long l = i.toEpochMilli();
System.out.println(l);
// ofEpochMilli 通过给定毫秒数获取Instant对象,相当于Date类的有参构造器new Date(long millis)
Instant instant = i.ofEpochMilli(1644282265743l);
System.out.println(instant);
和SimpleDateFormat类似,对时间进行解析和格式化。
方式3:最重要的一种方法
// 通过静态方法ofPattern创建DateTimeFormatter对象,相当于SimpleDateFormat的有参构造器
DateTimeFormatter d = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 格式化
String format = d.format(LocalDateTime.now());
System.out.println(format);
// 解析
TemporalAccessor d1= d.parse(format);
System.out.println(d1);
//方式一:预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
//格式化:日期-->字符串
LocalDateTime localDateTime = LocalDateTime.now();
String str1 = formatter.format(localDateTime);
System.out.println(localDateTime);
System.out.println(str1);//2020-05-10T18:26:40.234
//解析:字符串 -->日期
TemporalAccessor parse = formatter.parse("2020-05-10T18:26:40.234");
System.out.println(parse);
//方式二:
//本地化相关的格式。如:ofLocalizedDateTime()
//FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT :适用于LocalDateTime
DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
//格式化
String str2 = formatter1.format(localDateTime);
System.out.println(str2);//2020年5月10日 下午06时26分40秒
//本地化相关的格式。如:ofLocalizedDate()
//FormatStyle.FULL / FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT : 适用于LocalDate
DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);
//格式化
String str3 = formatter2.format(LocalDate.now());
System.out.println(str3);//2020-5-10