Java内部类与常用类

Java内部类与常用类

内部类

  • 成员内部类
  • 静态内部类
  • 局部内部类
  • 匿名内部类

什么是内部类

  • 概念:在一个类的内部再定义一个完整的类。
image-20230523220503352
package InnerClass.demo01;

// 身体
public class Body {
    // 头部 内部类也会生成class文件
    class Header{

    }
}
package InnerClass.demo01;

// 身体
public class Body {
    private String name;

    // 头部 内部类编译之后也会生成class字节码文件
    class Header{

        public void show() {
            // 内部类可以直接访问外部类的私有成员,而不破坏封装。
            System.out.println(name);
        }
    }
}
  • 特点
    • 内部类编译之后可以生成独立的字节码文件。
    • 内部类可以直接访问外部类的私有成员,而不破坏封装。
    • 内部类可以为外部类提供必要的内部功能组件。

成员内部类

  • 在类的内部定义,与实例变量、实例方法同级别的类。
  • 外部类的一个实例部分,创建内部类对象时,必须依赖外部类对象
Outer.Inner inner = outer.new Inner();
Outer.Inner inner = new Outer().new Inner();
  • 当外部类、内部类存在同名属性时,会优先访问内部类属性。如要优先访问外部类的同名属性要加:外部类名.this.属性名
package InnerClass.demo02;
// 外部类
public class Outer {
    // 实例变量 定义时可以在前面添加访问修饰符如private
    private String name = "张三";
    private int age = 20;


    // 内部类 也可以在前面添加访问修饰符
    class Inner{
        private String address = "北京";
        private String phone = "110";
        // 内部类属性和外部类的属性名字相同:
        private String name = "李四";

        // 方法
        public void show() {
            // 打印外部类的属性
            System.out.println(name);
            // 打印外部类的属性,内部类属性和外部类的属性名字相同,Outer.this
            System.out.println(Outer.this.name);
            System.out.println(Outer.this.age);
            // 打印内部类中的属性
            System.out.println(this.address);
            System.out.println(this.phone);
        }
    }

}
  • 成员内部类中不能定义静态成员
// 内部类中不能包含静态成员,会报错
private static String country = "中国";
// 但是可以包含静态常量
private static final String country = "中国";

静态内部类

  • 不依赖外部对象,可直接创建或通过类名访问,可声明静态成员。(相当于外部类),只有静态内部类才能加static关键词修饰,其他非内部类(普通类)是不能用static修饰的。
package InnerClass.demo03;

// 外部类
public class Outer {
    private String name = "李四";
    private int age = 18;

    // 静态内部类:和外部类相同级别
    static class Inner{
        private String address = "上海";
        private String phone = "111";
        // 静态成员
        private static int count = 1000;

        public void show() {
            // 调用外部类的属性
            // 1.先创建外部类对象
            Outer outer = new Outer();
            // 2.然后调用外部类对象的属性
            System.out.println(outer.name);
            System.out.println(outer.age);
            // 调用静态内部类的属性和方法,直接调用
            // 相当于调用自己的方法,属性名前面省略了this.
            System.out.println(address);
            System.out.println(this.phone);
            // 调用静态内部类的静态属性,直接:类名.属性名
            System.out.println(Inner.count);
        }
    }
}
package InnerClass.demo03;

public class TestOuter {
    public static void main(String[] args) {
        // 静态内部类就相当于外部类,所以直接创建静态内部类对象
        Outer.Inner inner = new Outer.Inner(); // 此处只是表示包含关系
        // 调用方法
        inner.show();
    }
}

局部内部类

  • 定义在外部类方法中,作用范围和创建对象范围仅限于当前方法。

  • 局部内部类访问外部类当前方法中的局部变量时,因无法保障变量的生命周期与自身相同,变量必须修饰为final

  • 限制的使用范围,局部内部类只能在当前方法中使用。

package InnerClass.demo04;

public class Outer {
    private String name = "刘同学";
    private int age = 21;

    public void show() {
        // 定义局部变量:肯定不能带访问修饰符~
        String address = "江苏"; // 默认加上final 意味着后面不能对其进行修改

        // 局部内部类:不能加任何访问修饰符
        class Inner{
            // 局部内部类的属性
            private String phone = "1222222222";
            private String email = "luck1y@qq.com";
//            private static int count = 100;  会报错 局部内部类中是不能包含静态成员的
            private final static int count = 100; //静态常量不会报错
            public void show2() {
                // 访问外部类的属性
                // 相当于省略了Outer.this
                System.out.println(name); // 如果show方法是静态的则要实例化外部类来访问其属性
                System.out.println(Outer.this.age);
                // 访问内部类的属性
                // 相当于省略了this.
                System.out.println(phone);
                System.out.println(this.email);
                // 访问局部变量,jdk1.7要求:变量必须是常量final,jdk1.8自动添加final
                System.out.println(address); // 其实这里的address已经写死为常量 “江苏”
            }
        }
        // 创建局部内部类对象
        Inner inner = new Inner();
        inner.show2();

    }
}
package InnerClass.demo04;

public class TestOuter {
    public static void main(String[] args) {
        // 局部内部类只能在对应的方法内使用,所以要先创建外部类
        Outer outer = new Outer();
        outer.show();
    }
}

匿名内部类

  • 没有类名的局部内部类(一切特征都与局部内部类相同)。

  • 必须继承一个父类或者实现一个接口的情况下使用较多。

  • 定义类、实现类、创建对象的语法合并、智能创建一个该类的对象。

  • 优点:减少代码量。

  • 缺点:可读性较差。

package InnerClass.demo05;
// 接口
public interface Usb {
    // 服务
    void service();
}
package InnerClass.demo05;

public class Mouse implements Usb {

    @Override
    public void service() {

        System.out.println("连接电脑成功,鼠标开始工作!");
    }
}
package InnerClass.demo05;

public class TestUsb {
    public static void main(String[] args) {
        // 创建接口类型的变量
        /*Usb usb = new Mouse();
        usb.service();*/

        // 局部内部类
        class Fan implements Usb {

            @Override
            public void service() {
                System.out.println("连接电脑成功,风扇开始工作!");
            }
        }
        // 使用局部内部类创建对象
//        Usb usb = new Fan();
//        usb.service();

        // 使用匿名内部类优化(相当于创建了一个局部内部类)
        // 匿名内部类的名字我们看不到,但是实际编译是有一个class文件的  叫做: TestUsb$1.class
        Usb usb = new Usb() {
            @Override
            public void service() {
                System.out.println("连接电脑成功,风扇开始工作!");
            }
        };
        usb.service();

    }
}

Object类

  • Object类又称为超类、基类,是所有类的直接或间接父类,位于继承树的最顶层。
  • 任何类,如果没有书写extends显示继承某个类,都默认直接继承Object类,否则为间接继承。
image-20230524211436244
  • Object类中所定义的方法,是所有对象都具备的方法。
  • Object类型可以存储任何对象。
    • 作为参数,可接受任何对象。
    • 作为返回值,可返回任何对象。

Object类常用方法

getClass()方法

public final Class<?> getClass(){
    
}
  • 返回引用中存储的实际对象类型。
package CommonClasses.demo01;

public class Student {
    private String name;
    private int age;
    public Student() {

    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
package CommonClasses.demo01;

public class TestStudent {
    public static void main(String[] args) {
        Student s1 = new Student("aaa" , 20);
        Student s2 = new Student("bbb" , 21);
        // 判断s1和s2是不是同一个类型
        Class class1 = s1.getClass();
        Class class2 = s2.getClass();
        if (class1 == class2) {
            System.out.println("s1和s2属于同一个类型");
        } else {
            System.out.println("s1和s2不是同一个类型");
        }
    }
}

结果:

s1和s2属于同一个类型

hashCode()方法

public int hashCode() {
    
}
  • 返回该对象的哈希码值,

  • 哈希值根据对象的地址字符串数字使用hash算法计算出来的int类型的数值。

  • 一般情况下相同对象返回相同的哈希码,用hashCode()方法来判断两个对象是否是同一个对象。

package CommonClasses.demo01;

public class TestStudent {
    public static void main(String[] args) {
        Student s1 = new Student("aaa" , 20);
        Student s2 = new Student("bbb" , 21);

        // hashCode方法
        System.out.println(s1.hashCode());

        System.out.println(s2.hashCode());
        Student s3 = s1;
        //s3和s1的hashCode值相同,指向的是同一个对象
        System.out.println(s3.hashCode());

    }
}

结果:

990368553
1096979270
990368553

toString()方法

public String toString() {
    
}
  • 返回该对象的字符串表示(表现形式)。

  • 可以根据程序需求覆盖该方法,如:展示对象各个属性值。

package CommonClasses.demo01;

public class TestStudent {
    public static void main(String[] args) {
        
        Student s1 = new Student("aaa" , 20);
        Student s2 = new Student("bbb" , 21);

        // 3.toString方法
        System.out.println(s1.toString());
        System.out.println(s2.toString());
    }
}

结果:

CommonClasses.demo01.Student@3b07d329
CommonClasses.demo01.Student@41629346

我们这里@后面的数字是hashCode但是和我们上面hashCode方法获得的值不一样,我们进入toString方法的源码

    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

可以看到源码部分将hashCode转为了16进制表示,hashCode得到的是十进制,二者实际上是一样的。

但有些时候,我们并不想看到原始的toString显示的东西,所以我们需要重写toString方法。

可以使用快捷键Alt + inert,也可以鼠标右键选择generate–>toString–>选中name和age属性即可。

package CommonClasses.demo01;

public class Student {
    private String name;
    private int age;
    public Student() {

    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

//    public String toString() {
//        return name + ";" + age;
//    }
    // 快捷键重写的toString方法
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

再次Run结果:

Student{name='aaa', age=20}
Student{name='bbb', age=21}

equals()方法

public boolean equals(Object obj) {
    
}
  • 比较两个对象(地址)是否相等,如果相等返回true,不相等返回false
package CommonClasses.demo01;

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

        Student s1 = new Student("aaa" , 20);
        Student s2 = new Student("bbb" , 21);

        // 4.equals方法:判断两个对象是否相等
        // 比较的是地址
        System.out.println(s1.equals(s2));

        Student s4 = new Student("小明", 17);
        Student s5 = new Student("小明", 17);
        System.out.println(s4.equals(s5));

    }
}

结果:

false
false

虽然新建了两个参数一样的Student对象s4和s5,但是equals方法返回的依然是false,说明equals方法是比较对象在堆中的地址的!

  • 这时候我们就可以对equals进行重写,来比较两个对象的内容是否相同。
  1. 比较两个引用是否指向同一个对象。
  2. 判断obj是否为null。
  3. 判断两个引用指向的实际对象类型是否一致。
  4. 强制类型转换。
  5. 依次比较各个属性值是否相同。
package CommonClasses.demo01;

public class Student {
    private String name;
    private int age;
    public Student() {

    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

//    public String toString() {
//        return name + ";" + age;
//    }
    // 快捷键重写的toString方法
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object obj) {
        // 1.判断两个对象是否是同一个引用
        if (this == obj) {
            return true;
        }
        // 2.判断obj是否为空
        if(obj == null) {
            return false;
        }
        // 3.判断是否是同一个类型
//        if (this.getClass() == obj.getClass()) {
//
//        }
        // instanceof 判断对象是否是某种类型
        if (obj instanceof Student) {
            // 4.强制类型转换
            Student s = (Student)obj;
            // 5.比较属性
            if (this.name.equals(s.getName()) && this.age == s.getAge()) {
                return true;
            }
        }
        return false;
    }
}

此时我们再次运行得到结果为:

false
true

finalize()方法

  • 当对象被判定为垃圾对象时,由JVM自动调用此方法,用以标记垃圾对象,进入回收队列。
  • 垃圾对象:没有有效引用指向此对象时,为垃圾对象。
  • 垃圾回收:有GC销毁垃圾对象,释放数据存储空间。
  • 自动回收机制:JVM的内存耗尽,一次性回收所有垃圾对象。
  • 手动回收机制:使用System.gc();通知JVM执行垃圾回收。

重写finalize()方法

package CommonClasses.demo01;

public class Student {
    private String name;
    private int age;
    public Student() {

    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
	// 重写finalize方法
    @Override
    protected void finalize() throws Throwable {
        System.out.println(this.name + "对象被回收了");
    }
}

提示:

image-20230525154142955

包装类

什么是包装类

  • 包装类是基本数据类型所对应的引用数据类型

  • 八种四类的基本数据类型,这八种基本数据类型并没有什么可以直接使用的方法,除了用运算符做一些操作。而且基本数据类型是存放在栈中的,有了包装类之后,将存放在堆中,在栈中引用。

  • 为了能对基本数据类型进行一些方法上的操作,Sun公司做了这些基本数据类型对应的引用数据类型,叫做基本数据类型的包装类,这样他们就拥有了属性和方法。

  • Object可以统一所有数据,包装类的默认值是null(引用数据类型的默认值)。

这八个基本数据类型对应的引用类型如下:

基本数据类型包装类型
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
booleanBoolean
charCharacter
  • Byte
  • Character
image-20230525160056856
  • Double
image-20230525160134478
  • Integer
image-20230525160147650 - **Long** image-20230525160157944 - **Float** image-20230525160213327 - **Short** image-20230525160224231 - **Boolean** image-20230525160237351 ### 类型转换与装箱、拆箱

基本类型变量–>栈;引用类型变量–>堆。

  • 装箱:基本类型转成引用类型。
  • 拆箱:引用类型转成基本类型。
  • 8中包装类提供不同类型间的转换方式。
    • Number父类中提供的6个共性方法。这六个方法可以用来拆箱,将引用类型转换为基本类型。
  • parseXXX()静态方法。可以实现字符串和基本类型之间的转换。
  • valueOf() 静态方法。可以实现基本类型转换为引用类型,即装箱。
  • 注意:需保证类型兼容,否则会抛出NumberFormatException异常。
image-20230525162159579

装箱拆箱操作演示:

package CommonClasses.demo02;

public class Demo01 {
    public static void main(String[] args) {
//        int num = 10;
        // 类型转换:装箱,基本类型转成引用类型的过程
        // 基本类型
        int num1 = 18;
        // 使用Integer类创建对象
        Integer integer1 = new Integer(num1);
        Integer integer2 = Integer.valueOf(num1);
        System.out.println("装箱");
        System.out.println(integer1);
        System.out.println(integer2);

        // 类型转换:拆箱,引用类型转成基本类型
        Integer integer3 = new Integer(100);
        int num2 =integer3.intValue();
        System.out.println("拆箱");
        System.out.println(num2);

        // 以上方式为JDK1.5之前的装箱拆箱操作
        // JDK1.5后,提供自动装箱和拆箱
        int age = 30;
        // 自动装箱
        Integer integer4 = age;
        System.out.println("自动装箱");
        System.out.println(integer4);
        // 自动拆箱
        int age2 = integer4;
        System.out.println("自动拆箱");
        System.out.println(age2);
    }
}

如何实现的自动装箱拆箱?

在IDEA中查看class文件:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package CommonClasses.demo02;

public class Demo01 {
    public Demo01() {
    }

    public static void main(String[] args) {
        int num1 = 18;
        Integer integer1 = new Integer(num1);
        Integer integer2 = Integer.valueOf(num1);
        System.out.println("装箱");
        System.out.println(integer1);
        System.out.println(integer2);
        Integer integer3 = new Integer(100);
        int num2 = integer3;
        System.out.println("拆箱");
        System.out.println(num2);
        int age = 30;
        Integer integer4 = Integer.valueOf(age);
        System.out.println("自动装箱");
        System.out.println(integer4);
        int age2 = integer4;
        System.out.println("自动拆箱");
        System.out.println(age2);
    }
}

可以发现其实是系统偷偷调用的Integer.valueOf();方法,不用我们手动操作了,实现了自动装箱拆箱操作。


基本类型和字符串之间的转换:

package CommonClasses.demo02;

public class Demo01 {
    public static void main(String[] args) {
//        int num = 10;
        // 类型转换:装箱,基本类型转成引用类型的过程
        // 基本类型
        int num1 = 18;
        // 使用Integer类创建对象
        Integer integer1 = new Integer(num1);
        Integer integer2 = Integer.valueOf(num1);
        System.out.println("装箱");
        System.out.println(integer1);
        System.out.println(integer2);

        // 类型转换:拆箱,引用类型转成基本类型
        Integer integer3 = new Integer(100);
        int num2 =integer3.intValue();
        System.out.println("拆箱");
        System.out.println(num2);


        // 以上方式为JDK1.5之前的装箱拆箱操作
        // JDK1.5后,提供自动装箱和拆箱
        int age = 30;
        // 自动装箱
        Integer integer4 = age;
        System.out.println("自动装箱");
        System.out.println(integer4);
        // 自动拆箱
        int age2 = integer4;
        System.out.println("自动拆箱");
        System.out.println(age2);

        System.out.println("===================");
        // 基本类型和字符串之间的转换
        // 1.基本类型转成字符串
        int n1 = 100;
        int n3 = 15;
        // 1.1使用 + 号
        String s1 = n1 + "";
        // 1.2使用Integer.toString()方法
        String s2 = Integer.toString(n1);
        String s4 = Integer.toString(n3, 16); // 使用Integer重载方法转为指定进制
        System.out.println(s1);
        System.out.println(s2);
        System.out.println("===================");
        System.out.println(n3);
        System.out.println(s4);

        // 2.字符串转成基本类型
        String str = "150"; // 这是字符串形式,如果这里不是数字,转换会报错:数字格式化错误
        // 使用Integer.parseXXX()
        int n2 = Integer.parseInt(str);
        System.out.println(n2);
        System.out.println("===================");
        // boolean字符串形式转成基本类型:"true"--->true 不是"true"--->false
        String str2 = "true";
        boolean b1 = Boolean.parseBoolean(str2);
        System.out.println(b1);

        String str3 = "liu";
        boolean b2 = Boolean.parseBoolean(str3);
        System.out.println(b2);
    }
}

整数缓冲区

  • Java预先创建了256个常用的整数包装类型对象。
  • 之所以使用整数缓存区,是因为在实际应用当中对常用的数字创建好之后方便复用,节省空间。
package CommonClasses.demo02;

public class Demo02 {
    public static void main(String[] args) {
        // 面试题
        Integer integer1 = new Integer(100);
        Integer integer2 = new Integer(100);
        System.out.println(integer1 == integer2); // false

        Integer integer3 = 100; // 自动装箱 实际调用的Integer.valueof(100)
        Integer integer4 = 100;
        System.out.println(integer3 == integer4); // true

        Integer integer5 = 200; // 自动装箱 实际调用的Integer.valueof(200)
        Integer integer6 = 200;
        System.out.println(integer5 == integer6); // false
    }
}

结果:

false
true
false

原因:Java预先创建了256个常用的整数包装类型对象。在Integer.valueOf()方法源码中可以看到:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
static final int low = -128;
static final int high; // 127
static final Integer[] cache;
static Integer[] archivedCache;

发现有个IntegerCache,即Integer缓冲区 ,它的最小值low为-128 ,最大值high为127,Java预先创建了一个 high-low+1 = 256 长度的数组在堆空间中,保存的数字为-128~127之间的数。

  • integer3 和 integer4 在创建的时候只是从上边已经创建好的数组中取值为100的地址,所以两个引用取到的值都是数组中那个100,地址是相同的,返回true。

  • integer5 和 integer6 在数组中找的时候没有找到200,因为最大值为127,而源码中在数组中找不到的时候就会 new Integer(200),重新创建一个对象,在堆中的地址不同,返回false。

  • Integer valueOf(int i)的部分源码:

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

String类

  • 字符串是常量,创建之后不可改变。

  • 字符串字面值存储在字符串池(常量池)中,可以共享。(字符串池就在方法区中,不同jdk版本不同)。

  • 学到这里,内存中至少有了:栈、堆、方法区。方法区在不同版本称呼不同,jdk1.8之后叫元空间,1.8之前叫永久代。

  • 新建字符串的两种方式

    1. String s = “hello”;产生一个对象,存储在字符串池中。
    2. String s = new String(“hello”);产生两个对象,堆、池中各存储一个。
package CommonClasses.demo02;

public class Demo03 {
    public static void main(String[] args) {
        // "hello" 常量存储在字符串池中。
        String name = "hello";
        // 在字符串池中又开辟一个空间,name指向zhangsan,hello若不用则会变成垃圾对象
        // 给字符串赋值时,并没有修改数据,二是重新开辟一块空间
        name = "zhangsan";
        String name2 = "zhangsan";

        // 字符串的另一种创建方式
        String str = new String("Java是最好的编程语言");
        String str2 = new String("Java是最好的编程语言");
        System.out.println(str == str2); // false 比的是地址
        // 在字符串中,equals先比较地址、地址不同则比较内容,最后返回true或者false
        System.out.println(str.equals(str2)); // true 在这里比的是数据即字符串内容
    }
}

结果:

false
true

在这里我们看equals的源码:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    return (anObject instanceof String aString)
            && (!COMPACT_STRINGS || this.coder == aString.coder)
            && StringLatin1.equals(value, aString.value);
}

可以看到在比较字符串时,是先比较的地址(==)是否相同,然后比较字符串中的值(内容)是否相同。

String常用方法

  • public int length() : 返回字符串的长度
  • public char charAt(int index) : 根据下标获取字符
  • public boolean contains(String str) : 判断当前字符串中是否包含str
  • public char[] toCharArray() : 将字符串转换成数组
  • public int indexOf(String str) : 查找str首次出现的下标,存在,则返回该下标;不存在,则返回-1
  • public int lastIndexOf(String str) : 查找字符串在当前字符串中最后一次出现的下标索引。
  • public String trim() : 去掉字符串前后的空格
  • public String toUpperCase() : 将小写转成大写
  • public boolean endWith(String str) : 判断字符串是否以str结尾
  • public String replace(char oldChar,char newChar) : 将旧字符串替换成新字符串
  • public String[] split(String str) : 根据str做拆分
  • 补充方法:
    • equals、compareTo 用来比较大小
    • subString(index) 将一个字符串从下标为index处截取,只取后面的
package CommonClasses.demo02;

import java.util.Arrays;

public class Demo03 {
    public static void main(String[] args) {
        // "hello" 常量存储在字符串池中。
        String name = "hello";
        // 在字符串池中又开辟一个空间,name指向zhangsan,hello若不用则会变成垃圾对象
        // 给字符串赋值时,并没有修改数据,二是重新开辟一块空间
        name = "zhangsan";
        String name2 = "zhangsan";

        // 字符串的另一种创建方式
        String str = new String("Java是最好的编程语言");
        String str2 = new String("Java是最好的编程语言");
        System.out.println(str == str2); // false 比的是地址
        // 在字符串中,equals先比较地址、地址不同则比较内容,最后返回true或者false
        System.out.println(str.equals(str2)); // true 在这里比的是数据即字符串内容
        System.out.println("======================");

        // 字符串方法的使用
        // 1.length();返回字符串的长度
        // 2.charAt(int index);返回某个位置的字符
        // 3.contains(String str);判断是否包含某个子字符串

        String content = "Java是世界上最好的Java编程语言,Java真香"; // 空格也算一个字符
        System.out.println(content.length());
        System.out.println(content.charAt(0));
        System.out.println(content.contains("Java"));
        System.out.println(content.contains("php"));
        System.out.println("======================");

        // 字符串方法的使用
        // 4.toCharArray();返回字符串对应的数组
        // 5.indexOf();返回字符串首次出现的位置下标,不存在则返回-1
        // 6.lastIndexOf();返回字符串最后一次出现的位置下标
        System.out.println(Arrays.toString(content.toCharArray()));
        System.out.println(content.indexOf("Java"));
        // 从下角标为4的位置开始找
        System.out.println(content.indexOf("Java", 4));
        System.out.println(content.lastIndexOf("Java"));
        System.out.println("======================");

        // 字符串方法的使用
        // 7.trim();去掉字符串前后的空格
        // 8.toUpperCase();把小写转成大写、toLowerCase();把大写转成小写
        // 9.endWith(str);判断是否以str结尾、startsWith(str)判断是否以str开头

        String content2 = "    Hello World   ";
        System.out.println(content2.trim());
        System.out.println(content2.toUpperCase());
        System.out.println(content2.toLowerCase());
        String filename = "hello.java";
        System.out.println(filename.endsWith(".java"));
        System.out.println(filename.startsWith("hello"));
        System.out.println("======================");

        // 10.replace(char old, char new);用新的字符或字符串来替换旧的字符或字符串
        // 11.split();对字符串进行拆分

        System.out.println(content.replace("Java", "php"));

        String say = "java is the best    programing language,java xiang";
        //中括号里有空格和逗号意思是同时按照这两个来拆分,+ 号表示空格或者逗号可以连续有好几个
        String[] arr = say.split("[ ,]+");
//        System.out.println(arr.length);
        for (String string :
                arr) {
            System.out.println(string);
        }
        System.out.println("======================");
        /*
        补充两个方法equals、a.compareTo(b)
        a.compareTo(b)按照字典顺序比较大小:相等返回0,
        a > b 返回 a - b 的ASCII码值差值
        a < b 返回 b - a 的ASCII码值差值
        可以利用这一特性,返回值大于0则 a < b
        返回值小于0则 a > b
        返回值等于0则 a == b
        */
        String s1 = "hello";
        String s2 = "HELLO";
        System.out.println(s1.equals(s2));
        // 忽略大小写比较
        System.out.println(s1.equalsIgnoreCase(s2));

        String s3 = "abc"; // 97
        String s4 = "xyz"; // 120
        System.out.println(s3.compareTo(s4));

        String s5 = "abc";
        String s6 = "abcxyz";
        System.out.println(s5.compareTo(s6));

        String s7 = "abc";
        String s8 = "abc";
        System.out.println(s7.compareTo(s8));
        System.out.println("======================");

        // 还有个subString方法
        String s9 = "Java is nice";
        // 只有一个参数是提取从该位置(beginIndex)到最后的为新的子串
        String s10 = s9.substring(5);
        System.out.println(s10);
        // 有两个参数则是提取从 beginIndex 到 endIndex 前为新的子串
        String s11 = s9.substring(5, 7);
        System.out.println(s11);
    }
}

案例演示

需求:

  • 已知 String str = “this is a text”;
  1. 将str中的单词单独获取出来
  2. 将str中的text替换为practice
  3. 在text前面插入一个easy
  4. 将每个单词的首字母改为大写
package CommonClasses.demo02;

public class Demo04 {
    public static void main(String[] args) {
        String str = "this is a text";
        // 1.将str中的单词单独获取出来
        String[] arr = str.split(" ");
        for (String string :
                arr) {
            System.out.println(string);
        }
        System.out.println("===============");

        // 2.将str中的text替换为practice
        String str2 = str.replace("text", "practice");
        System.out.println(str2);
        System.out.println("===============");

        // 3.在text前面插入一个easy
        String str3 = str.replace("text", "easy text");
        System.out.println(str3);
        System.out.println("===============");

        // 4.将每个单词的首字母改为大写
        for (int i = 0; i < arr.length; i++) {
            char first = arr[i].charAt(0);
            // 把第一个字符转成大写
            char upperFirst = Character.toUpperCase(first);
            String newString = upperFirst + arr[i].substring(1);
            System.out.print(newString + " ");
        }
    }
}

结果:

this
is
a
text
===============
this is a practice
===============
this is a easy text
===============
This Is A Text 

可变字符串

  • 由于String的不可变性,会产生大量垃圾,所以产生了String增强类,即开辟缓冲区,直接在缓冲区操作,提高字符串效率,也节省内存。

  • StringBuffer:可变长字符串,JDK1.0提供,运行效率慢,但是线程安全。

  • StringBuilder:可变长字符串,JDK5.0提供,运行效率快,但是线程不安全。(单线程推荐)

  • 和String的区别:

    1. 比String的效率高
    2. 比String节省内存
  • 下标原则:含头不含尾

package CommonClasses.demo02;

public class Demo05 {
    public static void main(String[] args) {
        // 单线程推荐使用 StringBuilder---> 效率高
//        StringBuffer sb = new StringBuffer();
        StringBuilder sb = new StringBuilder();
        // 1.append();追加
        sb.append("Java世界第一");
        System.out.println(sb.toString());
        sb.append("Java真香");
        System.out.println(sb.toString());
        sb.append("Java真不错");
        System.out.println(sb.toString());
        // 2.insert();添加
        sb.insert(0, "我在最前面");
        System.out.println(sb.toString());
        // 3.replace();可以指定位置的替换, 下标含头不含尾
        sb.replace(0, 5, "现在我是第一");
        System.out.println(sb.toString());
        // 4.delete();可指定位置的删除
        sb.delete(0, 6);
        System.out.println(sb.toString());
        // 5.reverse();反转
        sb.reverse();
        System.out.println(sb.toString());
        // 6.清空sb字符串
        sb.delete(0, sb.length());
        System.out.println(sb.length());
        if (sb.isEmpty()) {
            System.out.println("sb字符串已清空");
        }
    }
}

结果:

Java世界第一
Java世界第一Java真香
Java世界第一Java真香Java真不错
我在最前面Java世界第一Java真香Java真不错
现在我是第一Java世界第一Java真香Java真不错
Java世界第一Java真香Java真不错
错不真avaJ香真avaJ一第界世avaJ
0
sb字符串已清空

验证 StringBuilder 的效率高于 StringBuffer:

package CommonClasses.demo02;

public class Demo06 {
    public static void main(String[] args) {
        // 开始时间
        long start = System.currentTimeMillis();
        String string = "";
        // 用时:3111ms
//        for (int i = 0; i < 99999; i++) {
//            string += i;
//        }
//        System.out.println(string);
        // 用时:22ms
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 99999; i++) {
            sb.append(i);
        }
        System.out.println(sb.toString());
        long end = System.currentTimeMillis();
        System.out.println("用时:" + (end - start));
    }
}

可以看出,S和String比起来tringBuilder速度非常快。

BigDecimal类

  • 思考:以下程序输出结果是多少?
image-20220109210306225
package CommonClasses.demo02;

public class Demo07 {
    public static void main(String[] args) {
        double d1 = 1.0;
        double d2 = 0.9;
        System.out.println(d1 - d2);
    }
}

结果:

0.09999999999999998
  • double 和 float 存的是近似值,是近似值存储,经过各种算数运算之后会出现误差

  • 但特殊场景下不允许出现误差 ,因此需要借助BigDecimal类,它是一种精确存储。

  • 位置:java.math包中。

  • 作用:精确计算浮点数。

  • 创建方式:BigDecimal bd = new BigDecimail(“1.0”);

  • 方法:

    1. 加法:

      BigDecimal add(BigDecimal bd);

    2. 减法:

      BigDecimal subtract(BigDecimal bd);

    3. 乘法:

      BigDecimal multiply(BigDecimal bd);

    4. 除法:

      BigDecimal divide(BigDecimal bd);

      结果除不尽的话 会报异常

      所以除法用其他的构造方法:

      divide(BigDecimal bd,int scal,RoundingMode mode);

      • 参数scal:指定精确到小数点后几位。
      • 参数mode:指定小数部分的取舍模式,通常为四舍五入(BigDecimal.ROUND_HALF_UP)
package CommonClasses.demo02;

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

public class Demo07 {
    public static void main(String[] args) {
        double d1 = 1.0;
        double d2 = 0.9;
        System.out.println(d1 - d2);

        // 面试题
        double result = (1.4 - 0.5) / 0.9;
        System.out.println(result);

        // BigDecimal,大的浮点数的精确计算。
        BigDecimal bd1 = new BigDecimal("1.0");//这里的构造方法一定要用字符串,因为字符串是最准确的
        BigDecimal bd2 = new BigDecimal("0.9");
        // 减法
        BigDecimal r1 = bd1.subtract(bd2);
        System.out.println(r1);

        // 加法
        BigDecimal r2 = bd1.add(bd2);
        System.out.println(r2);

        // 乘法
        BigDecimal r3 = bd1.multiply(bd2);
        System.out.println(r3);

        // 除法
        BigDecimal r4 = new BigDecimal("1.4").subtract(new BigDecimal("0.5")).divide(new BigDecimal("0.9"));
        System.out.println(r4);

        // 除不尽的情况
        // BigDecimal r5 = new BigDecimal("20").divide(new BigDecimal("3"),2, BigDecimal.ROUND_HALF_UP);
        // 上面的 BigDecimal.ROUND_HALF_UP 方法已经过时,但也可用
        BigDecimal r5 = new BigDecimal("20").divide(new BigDecimal("3"),2, RoundingMode.HALF_UP);
        System.out.println(r5);
    }
}

Date类

  • Date表示特定的瞬间,精确到毫秒,Date类中的大部分方法都已经被Calendar类中的方法所取代。
  • 时间单位
    • 1秒=1000毫秒
    • 1毫秒=1000微秒
    • 1微秒=1000纳秒
image-20230526130601679 image-20230526130643422

很多都已经过时被替代了。

1970年是Unix操作系统元年。

image-20220109212946216 image-20220109213000784
package CommonClasses.demo03;

import java.util.Date;

public class Demo01 {
    public static void main(String[] args) {
        // 1.创建Date对象
        Date date1 = new Date();
        // 今天
        System.out.println(date1.toString());
        System.out.println(date1.toLocaleString());
        // 昨天
        Date date2 = new Date(date1.getTime() - (60 * 60 * 24 * 1000));
        System.out.println(date2.toLocaleString());
        // 2.方法 after(); before();
        boolean b1 = date1.after(date2);
        System.out.println(b1);
        boolean b2 = date1.before(date2);
        System.out.println(b2);

        // 3.比较 compareTo();
        int d = date1.compareTo(date2);
        System.out.println(d);
        int d1 = date2.compareTo(date1);
        System.out.println(d1);
        int d3 = date2.compareTo(date2);
        System.out.println(d3);

        // 4.比较是否相等equals();
        boolean b3 = date1.equals(date2);
        System.out.println(b3);
    }
}

结果:

Fri May 26 13:21:41 HKT 2023
2023526日 下午1:21:41
2023525日 下午1:21:41
true
false
1
-1
0
false

Calendar类

  • Calendar提供了获取或设置各种日历字段的方法。

  • 构造方法

    • protected Calendar(): 由于修饰符是protected,所以无法直接创建该对象。
  • 其他方法

image-20230526145636600 ```java package CommonClasses.demo04;

import java.util.Calendar;

public class Demo01 {
public static void main(String[] args) {
// 1.创建一个Calendar对象
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getTime().toLocaleString());
System.out.println(calendar.getTimeInMillis()); // 1970年到现在的毫秒值
// 2.获取时间信息
// 获取年
int year = calendar.get(Calendar.YEAR);
//Calendar.YEAR == Calendar.1
// 3.获取月,月从 0-11
int month = calendar.get(Calendar.MONTH);
// 日
int day = calendar.get(Calendar.DAY_OF_MONTH); // Calendar.Date
// 小时
int hour = calendar.get(Calendar.HOUR_OF_DAY); // HOUR 12 小时制 ;HOUR_OF_DAY 24小时制
// 分钟
int minute = calendar.get(Calendar.MINUTE);
// 秒
int second = calendar.get(Calendar.SECOND);
System.out.println(year + “年” + (month + 1) + “月” + day + “日” + hour + “:” + minute + “:” + second);
// 修改时间
Calendar calendar2 = Calendar.getInstance();
// 日改成了昨天
calendar2.set(Calendar.DAY_OF_MONTH, 25);
System.out.println(calendar2.getTime().toLocaleString());

    // 4.add法修改时间 增加1小时
    calendar2.add(Calendar.HOUR, 1);
    System.out.println(calendar2.getTime().toLocaleString());
    // 减少2小时
    calendar2.add(Calendar.HOUR, -2);
    System.out.println(calendar2.getTime().toLocaleString());

    // 5.补充方法
    int max = calendar2.getActualMaximum(Calendar.DAY_OF_MONTH);//当前这个月的最大一天
    int min = calendar2.getActualMinimum(Calendar.DAY_OF_MONTH);//当前这个月的最小一天
    // 5月最大31天 最小1天
    System.out.println(max);
    System.out.println(min);
}

}


结果:

```java
2023年5月26日 下午3:19:14
1685085554360
2023年5月26日15:19:14
2023年5月25日 下午3:19:14
2023年5月25日 下午4:19:14
2023年5月25日 下午2:19:14
31
1

SimpleDateFormat类

  • SimpleDateFormat是一个以与语言环境有关的方式格式化和解析日期的具体类。

  • 进行格式化(日期 -> 文本)、解析(文本 -> 日期)。

  • 常用的时间模式字母:

image-20230526152155169 ```java package CommonClasses.demo03;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo03 {
public static void main(String[] args) throws Exception {
// 1.创建SimpleDateFormat对象
// y–年 M–月
// 格式可变
// SimpleDateFormat sdf = new SimpleDateFormat(“yyyy年MM月dd日HH:mm:ss”);
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy/MM/dd”);
// 2.创建Date
Date date = new Date();
// 格式化date(把日期转成字符串)
String str = sdf.format(date);
System.out.println(str);
// 解析(把字符串转成日期) 格式与上面定义的格式不一样会报错
Date date2 = sdf.parse(“1990/05/26”);
System.out.println(date2);
}
}


结果:

```java
2023/05/26
Sat May 26 00:00:00 HKT 1990

System类

  • System系统类,主要用于获取系统的属性数据和其他操作,构造方法是私有的。里面的方法都是静态的,直接 System. 就可以使用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h3m2J6xg-1685107262345)(https://luck1y7.oss-cn-nanjing.aliyuncs.com/%E5%9B%BE%E5%BA%8A/image-20230526153741308.png)]

package CommonClasses.demo03;

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("回收了" + name + "   " + age);
    }
}
package CommonClasses.demo03;

public class Demo05 {
    public static void main(String[] args) {
        // 1.arraycopy,数组的复制
        // src:原数组
        // srcPos:从哪个位置开始复制,第一个位置为0
        // dest:目标数组
        // destPos:目标数组的位置
        // length:复制的长度
        int[] arr = {20, 18, 15, 8, 35, 26, 45, 90};
        int[] dest = new int[8];
        System.arraycopy(arr, 0, dest, 0, arr.length);

        for (int i = 0; i < dest.length; i++) {
            System.out.print(dest[i] + " ");
        }
        System.out.println();
//        Arrays.copyOf(); 其实这个方法也是使用的System.arraycopy,效率很高

        // 2.获取时间
        System.out.println(System.currentTimeMillis());
        // System.currentTimeMillis()方法实现计时
        long start = System.currentTimeMillis();
        for (int i = -99999999; i < 99999999; i++) {
            for (int j = -99999999; j < 99999999; j++) {
                int result = i + j;
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("用时:" + (end - start));


        new Student("aaa", 19);
        new Student("bbb", 19);
        new Student("ccc", 19);

        // 3.System.gc(); 告诉垃圾回收器回收垃圾
        System.gc();

        // 4.退出JVM
        System.exit(0); // 后面的代码不会再执行了

        System.out.println("程序结束了");
    }
}

总结

  • 内部类
    • 在一个类的内部再定义一个完整的类。
    • 成员内部类、静态内部类、局部内部类、匿名内部类。
  • Object类
    • 所有类的直接或间接父类,可存储任何对象。
  • 包装类
    • 基本数据类型所对应的引用数据类型,可以使Object类统一所有数据。
  • String类
    • 字符串是常量,创建之后不可改变,字面值保存在字符串池中,可以共享。
  • BigDecimal
    • 可以精确计算浮点数
  • Date
    • 特定时间
  • Calendar
    • 对Date的改进和完善。
  • SimpleDateFormat
    • 格式化时间
  • System
    • 系统类

" " + age);
}
}


```java
package CommonClasses.demo03;

public class Demo05 {
    public static void main(String[] args) {
        // 1.arraycopy,数组的复制
        // src:原数组
        // srcPos:从哪个位置开始复制,第一个位置为0
        // dest:目标数组
        // destPos:目标数组的位置
        // length:复制的长度
        int[] arr = {20, 18, 15, 8, 35, 26, 45, 90};
        int[] dest = new int[8];
        System.arraycopy(arr, 0, dest, 0, arr.length);

        for (int i = 0; i < dest.length; i++) {
            System.out.print(dest[i] + " ");
        }
        System.out.println();
//        Arrays.copyOf(); 其实这个方法也是使用的System.arraycopy,效率很高

        // 2.获取时间
        System.out.println(System.currentTimeMillis());
        // System.currentTimeMillis()方法实现计时
        long start = System.currentTimeMillis();
        for (int i = -99999999; i < 99999999; i++) {
            for (int j = -99999999; j < 99999999; j++) {
                int result = i + j;
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("用时:" + (end - start));


        new Student("aaa", 19);
        new Student("bbb", 19);
        new Student("ccc", 19);

        // 3.System.gc(); 告诉垃圾回收器回收垃圾
        System.gc();

        // 4.退出JVM
        System.exit(0); // 后面的代码不会再执行了

        System.out.println("程序结束了");
    }
}

总结

  • 内部类
    • 在一个类的内部再定义一个完整的类。
    • 成员内部类、静态内部类、局部内部类、匿名内部类。
  • Object类
    • 所有类的直接或间接父类,可存储任何对象。
  • 包装类
    • 基本数据类型所对应的引用数据类型,可以使Object类统一所有数据。
  • String类
    • 字符串是常量,创建之后不可改变,字面值保存在字符串池中,可以共享。
  • BigDecimal
    • 可以精确计算浮点数
  • Date
    • 特定时间
  • Calendar
    • 对Date的改进和完善。
  • SimpleDateFormat
    • 格式化时间
  • System
    • 系统类
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Luck1y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值