JAVA - 基础(一)

隐式转换

  • 基本的隐式转换

把一个取值范围小的数值或者变量,赋值给另一个取值范围大的变量

float 4个字节 为什么比 long 8 个字节类型的取值范围大?
答:小数的二进制存储形式,更加节省内存

https://blog.csdn.net/qq_48078182/article/details/120827238?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-1-120827238-blog-54691746.235^v38^pc_relevant_sort_base1&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-1-120827238-blog-54691746.235^v38^pc_relevant_sort_base1&utm_relevant_index=2

  • 运算中的隐式转换

1.取值范围小的数据和取值范围大的数据进行运算,小的会先提升为大的之后,再进行运算
2.byte/short/char 三种数据在运算的时候,都会提升为int,然后再进行运算

tips:
当字符 + 字符 或者 字符 + 数字时, 会把字符通过ASCCI 码表查询到对应的数字再进行计算

原码反码补码

思考为什么int 转 byte 会出现负数?

public class Demo2 {

    public static void main(String[] args) {
        int a = 130;
        byte b  = (byte) a;
        System.out.println(b); // out: -126
    }
}
  • 原码

运用原码进行计算的弊端:在遇到负数运算,会出现错误

如下: -10 + (+10) 的结果为 -20

正确的计算方法: 10的二进制补码 + (-10) 二进制补码

跨 0 问题:

因此出现了补码 : 反码 + 1

-128 没有对应具体的数字,因此一个 byte 表示的范围为 -128 ~ 127

因此:

计算机在运算的时候,都是以二进制补码的形式在计算

  • 反码
    正数的反码与其原码相同
    负数的反码是符号位不变,其他位取反

  • 补码:
    正数的补码与其原码相同
    负数的补码是其反码的末位加1

负数的补码求原码和原码求补码计算规则都是:求反码再加1

有了以上知识储备,便可以解释 int 130 强制转换为 byte为什么会出现 -126:

过程:130 二进制原码 => 求二进制补码 => 砍掉前3个字节 => 求最后一个字节的原码,即-126

JAVA 常量优化机制:

// 在编译的时候(javac)就会将 3和4 两个字面量进行运算,产生的字节码文件: byte b = 7;
byte b = 3 + 4;

swtich 结构变更

package com.study;

import java.util.Scanner;

public class SwtichDemo {


    public static void main(String[] args) {

        Scanner s = new Scanner(System.in);

        int num = s.nextInt();
        switch (num) {
            case 1:
            case 2:
            case 3:
            case 4:
            case 5:
                System.out.println("工作日");
                break;
            case 6:
            case 7:
                System.out.println("休息日");
                break;
        }
        
// JDK 14 之后新特性:        
// -----------------------------------------------------
        int num1 = s.nextInt();

        switch (num1) {
            case 1, 2, 3, 4, 5:
                System.out.println("工作日");
                break;
            case 6, 7:
                System.out.println("休息日");
        }

// -----------------------------------------------------
        int num2 = s.nextInt();
        switch (num2) {
            case 1, 2, 3, 4, 5 -> System.out.println("工作日")
            case 6,7 -> System.out.println("休息日");
        }
    }
}

String

案例1:

/*
字符串特点:

  1.String 不可改变,可以被共享
  2.字符串常量池(StringTable) JDK7之前位于方法区,JDK7&之后版本放入在堆内存
  3.字面量值创建字符串方式:查找StringTable 有没有,有就复用,没有才创建对象
*/

public class Demo3 {
    public static void main(String[] args) {
        String s1 = "abc";
        String s2 = "abc";
        System.out.println(s1 == s2);  //out: true
    }
}

案例2:

public class Demo3 {
    public static void main(String[] args) {

        String s1 = "abc";
        String s2 = "ab" + "c";
        System.out.println(s1 == s2); //out: ture ,常量优化机制,javac 编译时 已经将ab + c 拼接,class文件中 s2 = "abc"
    }
}

案例3:

public class Demo3 {
    public static void main(String[] args) {
        /*        
        字符串用 "+" 号拼接: 底层是新建StringBuilder对象进行拼接操作 => toString() 赋值给s3
         */
        String s1 = "abc";
        String s2 = "ab";
        String s3 = s2 + "c";
        System.out.println(s1 == s3); //out: false
    }
}

解析:

StringBuilder

public class Demo3 {
    /*
        1.一个可变的字符序列
        2.StringBuilder是字符串缓冲区,将其理解是容器,这个容器可以存储任意数据类型
          但是只要进入到这个容器,全部变成字符串
     */
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();

        sb.append("aaa");
        sb.append(111);
        sb.append(2.23);
        sb.append(true);
        System.out.println(sb); //out: aaa1112.23true

    }
}

其他作用: 提高字符串拼接的效率

public class Demo4 {

    public static void main(String[] args) {

        method2();
    }

    private static void method2() {
        long start = System.currentTimeMillis();

        StringBuilder sb = new StringBuilder();

        for (int i = 1; i < 100000; i++) {
            sb.append(i);
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start); // out: 6ms
    }

    private static void method1() {
        long start = System.currentTimeMillis();

        String s = "";

        for (int i = 1; i < 100000; i++) {
            s += i;
        }

        long end = System.currentTimeMillis();
        System.out.println(end - start);  //out: 2885ms
    }
}

继承

/*
1. 父类的构造方法子类无法继承
2. JAVA只支持单继承,不支持多继承,但是支持多重继承
3. 所有子类构造方法第一行默认都有一个super(); 初始化子类前必须前初始化父类
4. 父类的私有属性和方法子类可以继承到,但是无法访问
*/


重写(@Override)

注意事项

  • 父类私有方法不能被重写
  • 子类重写父类方法时,访问权限要大于等于父类

final

final 修饰成员变量的注意事项:

  1. final 修饰成员变量,不允许修饰默认值
  2. final 修饰成员变量的初始化时机:
    1)在定义的时候直接赋值
    2)在构造方法中完成赋值

abstract 关键字

/*
抽象类特点:
1.抽象类不能被实例化
2.抽象类存在构造方法,用户进行初始化操作
3.抽象类中可以存在普通方法
4.抽象 类的子类:
  1): 要么重写抽象类中的抽象党法
  2):要么是抽象类

*/


接口

/*
接口中的成员特点:
- 只能是常量,默认修饰符:public static final
- 构造方法:没有
- 成员方法:只能是抽象方法,默认修修饰符:public abstract  #TODO 新特性

类和接口的各种关系:
- 类和类的关系:继承关系,只能单继承,但是可以多层继承
- 类和接口的关系:实现关系,可以单实现,也可以多实现,还可以继承一个类的同时实现多个接口
- 接口和接口的关系:继承关系,可以单继承,也可以多继承

*/

接口新特性

JDK8新特性1:

允许在接口中定义非抽象方法,但需要使用default 关键字修饰,这些方法就是默认方法

作用:解决接口升级,比如接口中新加方法,之前的实现类必须要继续实现的问题

格式: public default void show(){}

注意事项:
1.默认方法不是抽象方法,所以不强制重写(但是可以被重写,重写的时候去掉default关键字)
2.public可以省略,default不能省略
3.如果实现了多个接口,多个接口中存在同名的默认方法,子类必须对该默认方法进行重写

JDK8新特性2:

接口中允许定义 static 静态方法

格式:public static void shwo(){}

注意事项:静态方法只能通过接口名调用,不能通过实现类类名或者对象名调用

JDK9新特性:

接口中允许定义private私有方法

格式1: private void show(){}
格式2: private static method(){}

package com.study.minterface;

public class NewInterface {
    public static void main(String[] args) {
        Test t = new Test();
        t.method();
    }
}


class Test implements A, B {

    @Override
    public void method() {  // 实现的接口存在同名的默认方法时,子类必须要进行覆盖
        A.super.method(); // 采用 父类.super.method() 的方式指明调用的哪个父类的方法
    }

}
interface A {
    public default void method() {
        System.out.println("inter a method run");
    }

    public static void show() {
        System.out.println("inter a static show run");
    }

    /**
     * JDK 9新特性:接口中可以存在私有方法
     */
    private void run(){
        System.out.println("inter a privte method run");
    }
}

interface B {
    public default void method() {
        System.out.println("inter b method run");
    }
}

抽象类和接口的对比

抽象类:是对事物的描述(描述事物)
接口:是对行为的描述(指定给规则)

多态

前提:

  • 有继承/实现关系
  • 有方法重写
  • 有父类引用指向子类对象

弊端:
无法调用子类的特有方法

/*
1.成员变量:编译看左边(父类),检查父类是否存在成员变量,不存在编译都不会通过,运行看左边(左边),
2.成员方法:编译看左边(父类),运行看右边(子类)
    在编译的时候会检查父类有没有这个方法:
     1)没有:编译出错
     2)有: 编译通过,运行会先检查子类有无该方法(继承 - 未复写,走夫类。实现 - 一定走子类)
3.静态方法:看父类
*/

多态中的静态方法演示:

package com.study;

public class Demo7 {

    public static void main(String[] args) {
        Fu f = new Zi();
        f.show();   //out: fu run ,  class 文件中 => Fu.show(); 
    }

}


class Fu {
    int num = 10;
    public static void show(){
        System.out.println("fu run");
    }
}

class Zi extends Fu{
    int num = 20;

    public static void show(){
        System.out.println("fu run");
    }
}

class 文件:

多态中的类型转换

package com.study;

public class Demo8 {

    /*
    向下转型调用子类特有方法:
     1. instanceof: 判断类型的关键字
     2.如果不判断类型就强转有可能会出现:ClasscastExecption
    
     */
    public static void main(String[] args) {
        Animal a = new Cat();

        if (a instanceof  Cat){
           Cat cat = (Cat) a;
           cat.catMouse();
        }
    }

}


abstract class Animal{
    public abstract void eat();
}

class Dog extends Animal{

    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }
}

class Cat extends Animal{

    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

    public void catMouse(){
        System.out.println("猫捉老鼠");
    }
}

代码块

使用 {} 括起来的代码被称为代码块

  • 局部代码块
    位置:方法中
    作用:限定变量的生命周期,及早释放,提高内存利用率

  • 构造代码块
    位置:类中方法外
    特点:每次构造方法执行的时候,都会执行该代码块中的代码,并且在构造方法执行前执行
    作用:将多个构造方法中相同给的代码,抽取到构造代码块中,提高代码的复用性

class文件中其实会将构造代码块放入到构造方法中:

  • 静态代码块
    位置:类中方法外
    特点:需要使用static关键字修饰,随着类的加载而加载,并且只执行一次
    作用:在加载的时候做一些数据初始化的操作
package com.study;

public class Demo9 {
    public static void main(String[] args) {
        Student s = new Student();
        Student s1 = new Student(2);  
      /*
        out:
        构造代码块 run..........
        无参构造 run
        构造代码块 run..........
        带参构造 run
       */
    } 
}

class Student{

    int num;

    {
        System.out.println("构造代码块 run..........");
    }

    public Student(){
        System.out.println("无参构造 run");
    }

    public Student(int num) {
        System.out.println("带参构造 run");
    }
}

内部类

创建内部类对象的格式:外部类.内部类 对象名 = new 外部类().new 内部类();

内部类成员访问:

  • 内部类中,访问外部类成员:直接访问,包括私有
  • 外部类中,访问内部类成员:需要创建对象
package com.study.inner;

public class InnerTest {
    public static void main(String[] args) {
        Outter.Inner oi = new Outter().new Inner();
        oi.test1();
    }
}

class Outter{
    int num = 10;
    private String name = "zhangsan";

    public void showNum(){
        Inner i = new Inner();   //外部类访问内部类的成员,需要创建对象
        System.out.println(i.num);
    }

    class Inner{
        int num = 20;
        public void show(){
            System.out.println("name:" + name); //可以直接访问外部类的私有成员
        }

        public void test1(){
            int num = 30;
            System.out.println(num);   //30
            System.out.println(this.num);   //20
            System.out.println(Outter.this.num);   //10 ,访问外部类成员的格式
         }
    }
}

为什么需要内部类?

静态内部类

package com.study.staticinnerclass;

public class StaticInnerClassDemo {
    public static void main(String[] args) {
        Outer.Inner oi = new Outer.Inner();  //  创建静态内部类的格式
        Outer.Inner.show();
    }
}

class Outer {

    int num1 = 10;
    static int num2 = 20;

    static class Inner {
        int num3 = 30;
        public static void show() {
            //System.out.println(num1); 无法访问,因为num1 不是静态变量
            System.out.println(num2);
        }
    }
}

匿名内部类

定义:匿名内部类本质上是一个特殊的局部内部类(定义在方法内部)
前提:需要存在一个接口或类
格式:
new 类名(){}: 代表继承这个类
new 接口名(){}: 代表实现这个接口

作用:可以让代码变得更加简洁(实现的接口方法不多的时候),在定义类的时候对其进行实例化

Lambda表达式

Lambda表达式 是JDK8 开始后的一种新语法形式

作用:简化匿名内部类的代码写法

Lambda 表达式的简化格式:
() -> {}
(匿名内部类被重方法的形参列表) -> { 被重写方法的方法体代码 }

Lambda 表达式只能简化函数式接口的匿名内部类的写法形式

函数式接口:接口中只存在一个抽象方法,可以被@FunctionInterface 标记

package com.study.lambdademo;

import java.util.Random;


/*
    Lambda表达式的省略写法:
     - 参数类型可以省略不写
     - 如果只有一个参数,参数类型可以省略,同时()也可以省略
     - 如果Lambda 表达式的方法体代码只有一行代码
        可以省略大括号不写,同时要省略分号
        此时,如果这行代码是return 语句,必须省略return 不写,同时也必须省略";"不写

 */
public class LambdaDemo {
    public static void main(String[] args) {
//        useHandler(new RandomHandler() {
//            @Override
//            public int getNum() {
//                return  new Random().nextInt(100) + 1;
//            }
//        });

        useHandler(() -> new Random().nextInt(100) + 1);
    }

    public static void useHandler(RandomHandler r){
        System.out.println(r.getNum());
    }
}

@FunctionalInterface
interface RandomHandler{

    int getNum();
}

Lambda表达式和匿名内部类的区别

使用限制不同:

  • 匿名内部类: 可以操作类、接口
  • Lambda表达式:只能操作函数式接口

实现原理不同:

  • 匿名内部类:编译之后,产生一个单独的.class字节码文件
  • Lambda:编译之后,没有一个单独的.class字节码文件

BigDemical

作用:解决JAVA中小数计算出现精度丢失的问题

package com.study.bigdecimal;

import java.math.BigDecimal;
import java.math.RoundingMode;

public class BigDecimalDemo {

    /*
     创建对象:
     1.public BigDemical(doublue val): 不推荐使用,无法保证小数运算的精确问题
     2.public BigDemical(String val)
     3.public static BigDemical valueOf(double val)

     常用方法:
     1.public BigDemical add(BigDemical b): 加法
     2.public BigDemical substract(BigDemical b): 减法
     3.public BigDemical multiply(BigDemical b): 乘法
     4.public BigDemical divide(BigDemical b): 除法
     5.public BigDemical divide(BigDemical b,精确位数,舍入模式)
     */
    public static void main(String[] args) {
//        double d1 = 0.1;
//        double d2 = 0.2;
//        System.out.println(d1 + d2);  //out: 0.30000000000000004 会出现精度问题

        // 如何解决以上问题? 可以使用BigDemical类
        BigDecimal d1 = BigDecimal.valueOf(0.1);
        BigDecimal d2 = BigDecimal.valueOf(0.2);

        System.out.println(d1.add(d2)); //out:0.3

        System.out.println(d1.subtract(d2));  //-0.1
        System.out.println(d1.multiply(d2)); //0.02
        System.out.println(d1.divide(d2)); //0.5
        //-------------------------------------------------
        BigDecimal d3 = new BigDecimal("10.0");
        BigDecimal d4 = new BigDecimal("3.0");
        /*
        除法除不尽的时候会抛出以下异常
        Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
	at java.base/java.math.BigDecimal.divide(BigDecimal.java:1780)
	at com.study.bigdecimal.BigDecimalDemo.main(BigDecimalDemo.java:37)
         */
//        System.out.println(d3.divide(d4));

        //因此需要进行保留小数,和摄入模式
        System.out.println(d3.divide(d4, 2, RoundingMode.HALF_UP)); //3.33,四舍五入
        System.out.println(d3.divide(d4, 2, RoundingMode.UP)); //3.34 ,向上进一位
        System.out.println(d3.divide(d4, 2, RoundingMode.DOWN));  //3.33 ,舍去2位后的数字

        Math.abs(d1.subtract(d2).doubleValue());  // 0.1 ,doubleValue()获得 BigDecimal 的基本数值
    }
}

包装类

package com.study;

public class Demo11 {
    
    public static void main(String[] args) {
//        Integer i = new Integer(3); 已过时,不推荐使用
        
        Integer integer2 = Integer.valueOf(10);  //推荐使用的初始化方式
        
        //获得包装类的基本数值
        int num = integer2.intValue();
        
        //将数字字符串转化为数字
        String s = "123";
        int num2 = Integer.parseInt(s);
        
        /*
        特性:
        自动装箱: 基本数值类型可以赋值给包装类
        自动拆箱: 包装类赋值给基本数据类型
         */
        
        int num3 = integer2;
        integer2 = num2;
    }
}

自动装箱中的缓存

package com.study;

public class Demo12 {
    
    /*
    自动装箱的时候会调用Integer.valueOf()方法:
        数值范围在-128 ~ 127之间,不会创建新的对象,而是从底层数组中直接获取
        不在会重新new出新的Inter对象
    */
    public static void main(String[] args) {
        Integer i1 = 127;
        Integer i2 = 127;
        System.out.println(i1 == i2);   //true

        Integer i3= 129;
        Integer i4 = 129;
        System.out.println(i3 == i4); //false
    }
}

valueOf 原码:

    @IntrinsicCandidate
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)  
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

Pattern

package com.study;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Demo13 {

    public static void main(String[] args) {
        String data = "先帝1创业2未半而中道3崩殂4,今5天下三分6,益州疲弊7,此8诚危急存亡之秋也。然9侍卫之臣不懈于内,忠志之士忘身10于外者,盖追先帝之殊遇11,欲报之于陛下也。诚宜12开张圣听13,以光14先帝遗德,恢弘15志士之气,不宜妄自菲薄16,引喻失义17,以塞忠谏之路也18。";
        Pattern p  = Pattern.compile("\\d");  // 创建Patter对象
        Matcher matcher = p.matcher(data);   // 获取配置器对象

        while(matcher.find()){   //进行查找
            System.out.println(matcher.group());  // 获得提取结果
        }
    }
}

时间API

JDK8 之前的和JDK8之后时间API的区别:

JDK8 之后的更新:

JDK8之后 LocalDateTime 的用法:

package com.study;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class Demo14 {

    public static void main(String[] args) {
        //获取当前时间
        LocalDateTime l = LocalDateTime.now();
        System.out.println(l); //2023-03-25T20:19:13.448012400

        System.out.println(l.getYear()); //2023
        System.out.println(l.getMonth().getValue()); //3
        System.out.println(l.getDayOfMonth()); //25

        //转LocalDate
        LocalDate localDate = l.toLocalDate();
        System.out.println(localDate); //2023-03-25


        //转LocalTime
        LocalTime localTime = l.toLocalTime();
        System.out.println(localTime);  //20:26:33.925741900


        //获取指定时间
        LocalDateTime l2 = LocalDateTime.of(2023, 4, 4, 11, 12, 11);
        System.out.println(l2); ////2023-04-04T11:12:11

        //修改时间
        System.out.println(l2.withYear(2024)); //2024-04-04T11:12:11

        //l2 加二年
        System.out.println(l2.plusYears(2)); //2025-04-04T11:12:11

        //l2 减一个月
        System.out.println(l2.minusMonths(1)); //2023-03-04T11:12:11

        System.out.println(l.isAfter(l2)); //false,判断时间是否在之后
        System.out.println(l.isBefore(l2)); //true,判断时间是否在之前
        System.out.println(l.equals(l2)); //false,判断两个时间是否相等
    }
}

  • DateTimeFormatter:
package com.study.time;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateTimeFormatterDemo {

    /*
    DateTimeFormatter: 用于时间的格式化和解析

    1.对象的获取:
        static DateTimeFormatter ofPattern(格式): 获取格式对象
    2.格式化:
        String format(时间对象): 按照指定方式格式化
    3.解析:
        LocalDateTime.parse("解析字符串",格式化对象)
        LocalDate.parse("解析字符串",格式化对象)
        LocalTime.parse("解析字符串",格式化对象)
     */
    public static void main(String[] args) {

        LocalDateTime now = LocalDateTime.now();

        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        //格式化
        String format = dateTimeFormatter.format(now);
        System.out.println(format); //2023-03-25 21:27:36

        //解析
        String s = "2008年08月08日";
        DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
        LocalDate parse = LocalDate.parse(s, dateTimeFormatter2);
        System.out.println(parse);  //2008-08-08

    }
}
  • Instant :在Java 中,Instant 表示时间线上的某个点。被称为"新纪元"的时间线原点被设置为穿过伦敦格林尼治皇家天文台的本初子午线所处时区的1970年1月1日的午夜。这与UNIX/POSIX 时间中使用的管理相同、从该原点开始,时间按照每天86400秒向前或向回度量,精确到纳秒,不包含任何时区信息
package com.study.time;

import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;

public class InstantDemo1 {


    public static void main(String[] args) {

        Date d = new Date();
        String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(d);
        System.out.println(format);  //系统当前时间:2023-03-25 21:19:45

        Instant now = Instant.now();
        System.out.println(now); //2023-03-25T13:19:45.751139400Z, 有时差
        
        //处理
        ZonedDateTime zonedDateTime = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
        System.out.println(zonedDateTime); //2023-03-25T21:19:45.755139900+08:00[Asia/Shanghai] 
        
       //获取当前时间戳
       long  timestamp = Instant.now().totoEpochMilli()
      
       //时间戳转为Instant
       long l = 1729906908960L
       Instant instant = Instant.ofEpochSecond(l); 
    }
}

需要注意1:

格式化Instant 对象 的时候如果不指定时区信息,会无法处理:

        Instant now = Instant.now();
        System.out.println(now);
        String format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(now);
        System.out.println(format);

https://segmentfault.com/q/1010000042944837/a-1020000042944839
正确处理方式:

        Instant now = Instant.now();
        System.out.println(now);
        String format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()).format(now);
        System.out.println(format);
  • ZoneId:
package com.study.time;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Set;

public class ZoneIdDemo {

    /*
    ZoneId类: 时区类

    常见方法:
        1.static Set<String> getAvaiableZoneIds(): 获取JAVA中支持的所有时区
        2.static ZoneId systemDefault(): 获取系统默认时区
        3.static ZoneId of(Sting zoneid): 获取一个指定的时区
     */
    public static void main(String[] args) {
        Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        System.out.println(availableZoneIds);
        System.out.println(availableZoneIds.size()); //602

        //获取系统默认的是时区
        System.out.println(ZoneId.systemDefault()); //Asia/Shanghai

        //获取一个指定的时区
        ZoneId of = ZoneId.of("America/Cuiaba");
        System.out.println(of); //America/Cuiaba

        ZonedDateTime zonedDateTime = Instant.now().atZone(of);
        System.out.println(zonedDateTime); //2023-03-25T09:41:33.042623300-04:00[America/Cuiaba] ,这个时区下的当前时间
    }
}

计算时间间隔的类:

package com.study.time;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

public class Chrono0UnitDemo {
    /*
    ChronoUnit:
        可用于在单个时间单位内测量一段时间,可以用于比较所有的时间单位,该类工具最全
        还有类似的类:
            - Duration: 用于计算两个 "时间" 间隔(秒,纳秒)
            - Period: 用于计算两个 "日期" 间隔(年、月、日)
     */
    public static void main(String[] args) {
        LocalDateTime l = LocalDateTime.now();
        System.out.println(l);  //2023-03-26T10:26:14.420824600

        LocalDateTime of = LocalDateTime.of(1994, 1, 9, 8, 8, 8, 8);

        System.out.println("相差的年份:" + ChronoUnit.YEARS.between(of, l)); //远的日期在前,近的日期在后
        System.out.println("相差的月份:" + ChronoUnit.MONTHS.between(of, l));
        System.out.println("相差的天数:" + ChronoUnit.DAYS.between(of, l));
        System.out.println("相差的小时:" + ChronoUnit.HOURS.between(of, l));
        System.out.println("相差的分钟:" + ChronoUnit.MINUTES.between(of, l));

        /*
        out:
            相差的年份:29
            相差的月份:350
            相差的天数:10668
            相差的小时:256034
            相差的分钟:15362057
         */
    }
}

https://cloud.tencent.com/developer/article/2071369
https://www.cnblogs.com/oktokeep/p/16769849.html

可变参数

package com.study.map;

import java.util.Arrays;

public class ChangeVarDemo {

    /*
    可变参数的细节:
    1.方法的形参中只能存在一个可变参数
    2.如果存在其他参数,可变参数只能方在形参的最后位置
    3.可变参数实际就是一个数组
     */
    public static void main(String[] args) {
        int sum = getSum2(1, 2, 3, 4, 5);
        System.out.println("sum:" + sum);
    }


    public static int getSum(int...num){
        System.out.println(num);  //   getSum(); =>[I@4eec7777 , 说明可变参数是一个数组
        return 0;
    }

//    public static int getSum2(int...num, int a){ //编译不会通过:Vararg parameter must be the last in the list
//        System.out.println(num);
//        return 0;
//    }

    public static int getSum2(int num,int...a){
        int sum = 0;
        for (int i : a) {
            System.out.println(i);
            sum += i;
        }
        return sum;
    }
    /*
    输出:
      2
      3
      4
      5
      sum:14
    */
}

不可变集合

JDK 9 新加特性:

package com.study.map.immutable;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ImmutableDemo {

    public static void main(String[] args) {
        //不可变List
        List<String> list = List.of("zhangsan", "lisi", "wangwu", "zhaoliu");
//        list.remove("zhangsan");  运行会抛出:java.lang.UnsupportedOperationException

        //不可变set
        Set<String> set = Set.of("小明", "旺财");
//        set.remove("小明") 运行会抛出:java.lang.UnsupportedOperationException

        //不可变map
        Map<String, String> map = Map.of("name", "十三项", "age", "18");  //最多10个entry
//        map.put("address","河北省廊坊市");运行会抛出:java.lang.UnsupportedOperationException


        //想存放更多的数据可以采用copyof方法
        HashMap<String, String> hashMap = new HashMap<>();
        hashMap.put("1","2");
        hashMap.put("3","4");
        hashMap.put("5","6");
        hashMap.put("7","8");

        Map<String, String> map1 = Map.copyOf(hashMap);
//        map1.put("9","10");
    }
}

异常

异常的作用:

  • 异常是用来查询BUG的关键参考信息
  • 异常可以作为方法内部的一种特殊返回值以便通知调用者底层的执行情况
package com.study.exceptiondemo;

import java.security.spec.ECField;

public class ExceptionDemo1 {

    /*
    异常使用细节:
    1.如果捕获的异常存在父子关系,那么父类一定要写在子类的下面
    2.JDK7之后,如果要捕获多个异常,并且多个异常的处理方案相同,可以使用 | 进行同时捕获
     */
    public static void main(String[] args) {

        int[] arr = {1,2,2,3};

        try{
            int a = arr[10];
            String s = null;
            boolean fasdf = s.equals("fasdf");
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("ArrayIndexOutOfBoundsException");
        }catch (NullPointerException e){
            System.out.println("NullPointerException");
        }catch (Exception e){   // 异常中存在父子关系的,父类一定要放在下面,否则编译不通过
            System.out.println(e);
        }

        System.out.println("-------------------------------------------------------");

        int[] arr2 = {1,2,2,3};

        try{
            int a = arr[10];
            String s = null;
            boolean fasdf = s.equals("fasdf");
        }catch (ArrayIndexOutOfBoundsException | NullPointerException e){
            System.out.println("ArrayIndexOutOfBoundsException | NullPointerException");
        }
    }
}

异常抛出处理

  • throws:写在方法定义处,表示声明一个异常,告诉调用者,使用本方法可能会有哪些异常。

    • 编译时异常:必须要写
    • 运行时异常:可以不写
  • thorw:写在方法内,结束方法。手动抛出异常对象,交给调用者,方法中下面的代码不再执行了

自定义异常

  • 1.定义异常类
  • 2.写继承关系:运行时异常: 继承RunTimeExecption,编译时异常: 继承Eexeption
  • 3.空参构造
  • 4.带参构造

意义:就是为了让报错的信息更加见名之意

异常总结:

posted @ 2023-03-02 22:59  chuangzhou  阅读(11)  评论(0编辑  收藏  举报