JAVA SE 自我总结

目录

[1. 字面常量](#1. 字面常量)

[2. 数据类型](#2. 数据类型)

[3. 变量](#3. 变量)

[4. 类型转换](#4. 类型转换)

[5. 实参和形参的关系](#5. 实参和形参的关系)

[6. 数组](#6. 数组)

[6.1 数组的概念](#6.1 数组的概念)

[6.2 动态初始化](#6.2 动态初始化)

[6.3 静态初始化](#6.3 静态初始化)

[7. 数据区](#7. 数据区)

​编辑

[8. 数组的拷贝](#8. 数组的拷贝)

[8.1 赋值拷贝](#8.1 赋值拷贝)

[8.2 方法拷贝](#8.2 方法拷贝)

[9. 代码块](#9. 代码块)

[10. 内部类](#10. 内部类)

[10.1 实例内部类](#10.1 实例内部类)

[10.2 静态内部类](#10.2 静态内部类)

[10.3 匿名内部类](#10.3 匿名内部类)

[10.3.1 匿名内部类核心特性](#10.3.1 匿名内部类核心特性)

[11. 继承](#11. 继承)

[12. 向上转型和向下转型](#12. 向上转型和向下转型)

[12.1 向上转型](#12.1 向上转型)

[12.2 向下转型](#12.2 向下转型)

[13. 抽象类](#13. 抽象类)

[14. 接口](#14. 接口)

[15. 接口与抽象类区别与联系](#15. 接口与抽象类区别与联系)

[16. Object 类](#16. Object 类)

[17. String 类](#17. String 类)

[17.1 String 对象的比较](#17.1 String 对象的比较)

[17.2 字符串查找](#17.2 字符串查找)

[17.3.1 数值和字符串转化](#17.3.1 数值和字符串转化)

[17.3.2 大小写转换](#17.3.2 大小写转换)

[17.3.3 字符串转数组](#17.3.3 字符串转数组)

[17.4 字符串替换](#17.4 字符串替换)

[17.5 字符串拆分](#17.5 字符串拆分)

[17.6 字符串截取](#17.6 字符串截取)

[17.7 字符串的不可变性](#17.7 字符串的不可变性)

[17.8 字符串修改](#17.8 字符串修改)

[17.9 StringBuilder和StringBuffer](#17.9 StringBuilder和StringBuffer)


1. 字面常量

常量即程序运行期间, 固定不变的量成为常量, 比如以下代码

java 复制代码
System.Out.println("Hello World");

2. 数据类型

在 JAVA 中数据类型主要分两类: 基本数据类型和引用数据类型

基本数据类型有 四类: 整形,浮点型,字符型,布尔型

八种:字节型,短整型,整形,长整型,单精度浮点型,双精度浮点型,字符型,布尔型。

  1. 不论是16位系统还是32位系统,int 都只占用4个字节,long 都占8个字节;

  2. 整形和浮点型都是带有符号的;

  3. 整形默认为 int,浮点默认为 double;

引用数据类型分三种:类,接口,数组, 值得注意的是数组是定长的,除非重新初始化否则是不能改变的。

Java 中引用类型在堆里,基本类型在栈里。基本数据创建的类型称为基本变量,该变量空间存放的是其所对应的值,而引用数据类型则存的是对象所在的空间的地址

3. 变量

3.1 变量概念: 在 JAVA 程序中经常改变的内容,变量使用前必须要赋初值,否则会报错。

注意事项:

  1. 整形变量赋值不能超过该变量表示范围,否则会导致溢出。

  2. 变量使用前必需赋值,但是成员变量使用前可以不赋值,原因是当对象被创建时,系统会自动赋予默认值。

4. 类型转换

自动转换: 代码不需要进行处理,在编译时,编译器会自行处理。特点: 数据范围小的会自动向数据范围大的转换。

强制类型转换:当大范围向小范围变量转换时,需要我们手动处理,不能自动完成

5. 实参和形参的关系

Java 中形参用于接受值,形参是方法在定义时需要借助的变量,用来保存方法被调用时需要接收的值。实参的值永远都是拷贝到形参中,形参和实参本质是两个实体。

6. 数组

6.1 数组的概念

相同元素的集合,在内存中是一段连续的空间,数组中存放的元素类型相同,每个空间有自己的编号,处置下标从0开始。

6.2 动态初始化

在创建数组时, 直接指定数组中元素的个数

java 复制代码
int[] array = new int[10];
6.3 静态初始化

在创建数组时不指定个数,直接写入具体的数据内容。

java 复制代码
int[] array1 = new int[]{0,1,2,3,4,5,6,7,8,9};
double[] array2 = new double[]{1.0, 2.0, 3.0, 4.0, 5.0};
String[] array3 = new String[]{"hell", "Java", "!!!"};

静态初始化时,{}中数据类型必须与前[]数据类型相同,可以省略 new 数据类型。

7. 数据区

程序计数器:小空间用来保存下一条执行的指令的地址

虚拟机栈:每个方法执行时,都会先创建一个栈帧,栈帧包含 局部变量、操作数栈、动态链接、返回地址等一些信息。保存的都是方法执行时的信息,比如局部变量在方法结束后就会被销毁,因为栈帧被销毁了,栈帧中的数据就没了。

本地方法栈:保存的内容是 Native 方法的局部变量

:JVM 所管理的最大内存区域, 使用 new 创建的对象都在堆上保存, 堆随着程序开始运行时创建, 随着程序的退出而销毁。

方法区:用于储存被虚拟机加载的类信息、常量、静态变量

8. 数组的拷贝

8.1 赋值拷贝
java 复制代码
int[] arr = {1,2,3,4,5};
int[] copy = arr;

结果如下:

8.2 方法拷贝
java 复制代码
int[] arr = {1,2,3,4,5};
int[] newArr = Arrays.copyOf(arr,arr.length)

结果如下:

9. 代码块

代码块分为:普通代码块、构造块、静态块、同步代码块

普通代码块:定义在方法中的代码块

构造代码块:定义在类中的代码块,也叫实例代码块,一般用于初始化实例成员变量

静态块:使用 static 定义的代码块,一般用于初始化静态成员变量

  1. 静态代码块不管生成多少个对象都只会执行一次;

  2. 静态成员变量是类的属性,在 JVM 加载类时申请空间并初始化:

  3. 实例代码块只有在创建对象时才会执行;

执行顺序:静态代码块>实例代码块>构造方法

10. 内部类

内部类概念:可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。

10.1 实例内部类

实例内部类:没有被 static 修饰的成员内部类

  1. 在实例内部类中,可以任意访问外部类中的成员;

  2. 如果外部类和实例内部类中具有相同名称成员时,优先访问内部类的;

  3. 如果要让问同名称外部类成员, 必须: 外部类名称.this.同名成员名字

10.2 静态内部类

静态内部类:被 static 修饰的内部类

创建静态内部类实例:

java 复制代码
public class OutClass {
    static class InnerClass {
        public void methodInner() {
        }
    }
    public static void main(String[] args) {
    // 静态内部类对象创建 & 成员访问
    OutClass.InnerClass innerClass = new OutClass.InnerClass();
    innerClass.methodInner();
    }
}

在静态内部类中,只能访问外部类静态成员;

要想访问非静态成员,可以在静态内部类中添加一个外部类的实例变量,在构造内部类时传入外部类的实例,然后通过这个实例访问其非静态成员。

10.3 匿名内部类

匿名内部类:无法定义构造方法,只能在创建的时候实例化一次,通常用于覆盖父类方法或者实现接口的方法。在创建的时候要么继承一个类,要么实现一个接口,并且只能继承一个类或者实现一个接口。

在匿名内部类中,访问外部变量需要final修饰

Java 8 引入了 lambda 表达式,可以更简洁的表示函数式接口 的实现,但是 lambda 只能用于函数式接口 ,也就是只有一个抽象方法的接口 ,也就是说 lambda 只能用于口接,无法用于抽象类;而匿名内部类可以用于任何接口或类。

使用匿名内部类情况:

java 复制代码
// 非函数式接口(两个抽象方法)
interface MultiMethodInterface {
    void method1();
    void method2();
}

MultiMethodInterface obj = new MultiMethodInterface() {
    @Override
    public void method1() {
        System.out.println("实现方法1");
    }

    @Override
    public void method2() {
        System.out.println("实现方法2");
    }
};
obj.method1(); // 输出:实现方法1

总结:

10.3.1 匿名内部类核心特性

(1) 无显示类名,可直接在实例化时通过 new 关键字定义,无需单独声明类

(2) 继承或实现接口

继承类:new 父类(){重写方法}

实现接口:new 接口(){实现方法}

(3) 单次使用,仅在定义时实例化一次,无法重复使用

(4) 访问外部变量限制:访问外部方法的局部变量时,变量必须被 final 修饰

11. 继承

有以下注意点:

  1. 访问的成员变量子类中有, =优先访问自己的成员变量;

  2. 访问的成员变量子类中无, 则访问父类继承下来的, 如果父类也没有定义, 则编译报错;

  3. 如果子类父类成员变量同名, 优先访问自己的;

访问子类父类方法优先级同上, 当明确传参时, 优先匹配参数类型合理的方法

子类父类代码块执行顺序如下:

父类静态块>子类静态块>父类实例代码块>父类构造方法>子类实例代码块>子类构造方法

final 修饰的变量或者字段不能被修改,修饰类则此类不能被继承

12. 向上转型和向下转型

12.1 向上转型

向上转型是就是创建一个子类对象,将其当成父类对象来使用。向上转型为了代码实现更简单灵活,但是不能调用到子类特有的方法。

12.2 向下转型

父类引用指向子类对象,可以调用子类特有的方法。因为有可能出现ClassCastException的风险,所以必须用instanceof检查。

13. 抽象类

注意事项:

  1. 抽象类不能直接实例化对象;

  2. 抽象发放不能是private;

  3. 抽象方法不能被 final 和 static 修饰, 因为抽象方法需要被子类重写;

  4. 抽象类被继承时必须要重写父类中的抽象方法;

  5. 抽象类中不一定包含抽象方法, 但是有抽象方法的类一定是抽象类;

  6. 抽象类可以有构造方法,供子类创建对象时初始化父类的成员变量;

抽象类无需抽象方法的情况可能包含:代码服用、限制实例化、预留拓展性

14. 接口

注意事项:

  1. 接口类型是一种引用类型,但是不能直接 new 接口对象;

  2. 接口中的每一个方法都会被隐性指定为 public abstract;

  3. 接口中的方法不能再接口中实现, 只能由实现类来实现;

  4. 重写接口中方法时不能使用默认的访问权限;

  5. 接口中可以有变量, 但是会被指定为 public static final 变量

  6. 接口中不能有静态代码和构造方法

  7. 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类

  8. 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class

  9. jdk8中:接口中还可以包含default方法。

在 Java 中, 类和类之间是单继承, 一个类只能有一个父类, 但是一个类可以实现多个接口, 接口与接口之间可以多继承。即:用接口可以达到多继承的目的。

15. 接口与抽象类区别与联系

  1. 抽象类可以有构造方法、具体方法、成员变量、和抽象方法;但是在Java8之前, 接口都只有抽象方法和常量,Java8之后可以有默认方法和静态方法,但是不能有成员变量。

  2. 抽象类只能单继承, 接口可以多继承。抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中 不能包含普通方法, 子类必须重写所有的抽象方法.

  3. 使用场景上抽象类适合定义一些共同方法,但是希望由子类实现;而接口适合定义行为契约,不关心具体实现,允许多个不同类实现同一个接口,增加灵活性。 另一方面,接口由于多继承,适合在大型项目中定义模块之间的交互契约,而抽象类适合在具有层次接口的类之间共享代码。

抽象类 是 is-a 的关系,强调代码复用层次化设计

接口 是 can-do 的关系,强调行为解耦(不关心继承来的方法代码实现)

实际项目中,二者常结合使用

例如:抽象类实现接口(例如日志模块设计),提供公共逻辑。

下方为日志模块设计举例

java 复制代码
// 接口定义日志行为
interface Logger {
    void log(String message);
    void error(String message);
    void debug(String message);
}

// 抽象类实现接口,提供默认实现
abstract class AbstractLogger implements Logger {
    protected String logPrefix;  // 共享字段

    public AbstractLogger(String prefix) {
        this.logPrefix = prefix;
    }

    @Override
    public void log(String message) {
        System.out.println(logPrefix + "[INFO] " + message);  // 公共实现
    }

    // error() 和 debug() 留给子类实现
    @Override
    public abstract void error(String message);
    @Override
    public abstract void debug(String message);
}

// 子类只需实现差异化方法
class FileLogger extends AbstractLogger {
    public FileLogger() {
        super("[文件日志]");
    }

    @Override
    public void error(String message) {
        System.out.println(logPrefix + "[ERROR] " + message + " (写入文件)");
    }

    @Override
    public void debug(String message) {
        System.out.println(logPrefix + "[DEBUG] " + message + " (写入文件)");
    }
}

class DatabaseLogger extends AbstractLogger {
    public DatabaseLogger() {
        super("[数据库日志]");
    }

    @Override
    public void error(String message) {
        System.out.println(logPrefix + "[ERROR] " + message + " (写入数据库)");
    }

    @Override
    public void debug(String message) {
        System.out.println(logPrefix + "[DEBUG] " + message + " (写入数据库)");
    }
}

16. Object 类

Object 类是 Java 中所有类的超类, 除了 Object 本身,其他类都是直接或间接继承 Object,

**需要举例说明:**比如自定义类如何隐式继承Object,以及如何覆盖Object的方法。比如,创建一个Person类,不显式继承任何类,但它默认继承Object,可以重写toString、equals、hashCode方法。

需要注意的是:

  1. 用户创建的类(包括抽象类)无论是否显式继承其他类,最终都会直接或间接继承Object;

  2. 接口不继承 Object 类, 他们的父接口只能是其它接口;(比如,定义一个接口A,它不会自动拥有Object的方法,比如toString()或equals(),除非显式声明或者通过其他方式。

  3. 实现接口的类仍然继承 Object , 因为类必须继承自 Object。

在Java中,接口中的默认方法可能覆盖Object的方法,但这在Java 8之后是不允许的。例如,不能在接口中定义默认的toString()方法,因为这会与Object中的方法冲突。所以接口不能有与Object方法签名相同的默认方法(Default Method),但可以声明抽象方法,这种情况下实现类必须自己实现这些方法,即使Object中存在相同的方法。

如果两个对象相等(根据equals方法),他们的hashcode必须相同,反之hashCode相同不代表两个对象相等。

17. String 类

String 是引用类, 内部并不存储字符串本身

17.1 String 对象的比较

(1) 使用 == 比较,对于引用类型,比较的是引用中的地址;

java 复制代码
public static void main(String[] args) {
    int a = 10;
    int b = 20;
    int c = 10;
 
    // 对于基本类型变量,==比较两个变量中存储的值是否相同
    System.out.println(a == b);    // false
    System.out.println(a == c);    // true
 
    // 对于引用类型变量,==比较两个引用变量引用的是否为同一个对象
    String s1 = new String("hello");
    String s2 = new String("hello");
    String s3 = new String("world");
    String s4 = s1;
    System.out.println(s1 == s2);   // false
    System.out.println(s2 == s3);   // false 
    System.out.println(s1 == s4);   // true
}

(2) boolean equals(Object anObject) 方法:按照字典序比较

字典序:字符大小的顺序

String 类重写了父类 Object 中 equals 方法,Object 中 equals 默认按照==比较。String 重写 equals 之后按照字母大小比较。

java 复制代码
public static void main(String[] args) {
    String s1 = new String("hello");
    String s2 = new String("hello");
    String s3 = new String("Hello");
 
    // s1、s2、s3引用的是三个不同对象,因此==比较结果全部为false
    System.out.println(s1 == s2);       // false
    System.out.println(s1 == s3);       // false
 
    // equals比较:String对象中的逐个字符
    // 虽然s1与s2引用的不是同一个对象,但是两个对象中放置的内容相同,因此输出true
    // s1与s3引用的不是同一个对象,而且两个对象中内容也不同,因此输出false
    System.out.println(s1.equals(s2));  // true
    System.out.println(s1.equals(s3));  // false
}

(3) int compareTo(String s) 方法:按照字典序比较

与 equals 不同的是, equals 返回的是 boolean 类型,而 compareTo 返回的是 int 类型;

  1. 先按照字典序比较大小,如果出现不等字符,直接返回大小差值

  2. 如果前K个字符相等,返回值两个字符串差值

java 复制代码
public static void main(String[] args) {
    String s1 = new String("abc");
    String s2 = new String("ac");
    String s3 = new String("abc");
    String s4 = new String("abcdef");
    System.out.println(s1.compareTo(s2));   // 不同输出字符差值-1
    System.out.println(s1.compareTo(s3));   // 相同输出 0
    System.out.println(s1.compareTo(s4));   // 前k个字符完全相同,输出长度差值 -3
}

(4) int compareToIgnoreCase(String str) 方法: 与 compareTo 方式相同,但是忽略大小写比较。

java 复制代码
public static void main(String[] args) {
    String s1 = new String("abc");
    String s2 = new String("ac");
    String s3 = new String("ABc");
    String s4 = new String("abcdef");
    System.out.println(s1.compareToIgnoreCase(s2));   // 不同输出字符差值-1
    System.out.println(s1.compareToIgnoreCase(s3));   // 相同输出 0
    System.out.println(s1.compareToIgnoreCase(s4));   // 前k个字符完全相同,输出长度差值 -3
}
17.2 字符串查找
java 复制代码
public static void main(String[] args) {
    String s = "aaabbbcccaaabbbccc";
    System.out.println(s.charAt(3));                             // 'b'
    System.out.println(s.indexOf('c'));                          // 6
    System.out.println(s.indexOf('c', 10));                      // 15
    System.out.println(s.indexOf("bbb"));                        // 3
    System.out.println(s.indexOf("bbb", 10));                    // 12
    System.out.println(s.lastIndexOf('c'));                      // 17
    System.out.println(s.lastIndexOf('c', 10));                  // 8
    System.out.println(s.lastIndexOf("bbb"));                    // 12
    System.out.println(s.lastIndexOf("bbb", 10));                // 3
17.3.1 数值和字符串转化
java 复制代码
public static void main(String[] args) {
    // 数字转字符串
    String s1 = String.valueOf(1234);
    String s2 = String.valueOf(12.34);
    System.out.println(s1);
    System.out.println(s2); 
    System.out.println(s3);                
    System.out.println("=================================");    
    // 字符串转数字   
    // 注意:Integer、Double等是Java中的包装类型
    int data1 = Integer.parseInt("1234");    
    double data2 = Double.parseDouble("12.34");    
    System.out.println(data1);   
    System.out.println(data2); 
}
17.3.2 大小写转换
java 复制代码
public static void main(String[] args) {    
    String s1 = "hello";    
    String s2 = "HELLO";    
    // 小写转大写    
    System.out.println(s1.toUpperCase());    
    // 大写转小写    
    System.out.println(s2.toLowerCase()); 
}
17.3.3 字符串转数组
java 复制代码
public static void main(String[] args) {    
    String s = "hello";    
    // 字符串转数组    
    char[] ch = s.toCharArray();    
    for (int i = 0; i < ch.length; i++) {        
        System.out.print(ch[i]);   
    }    
    System.out.println();    
    // 数组转字符串    
    String s2 = new String(ch);    
    System.out.println(s2); 
}
17.4 字符串替换

使用一个指定的新字符串替换掉已有的字符串,可以用以下方法

17.5 字符串拆分

可以将一个完整的字符串按照指定的分隔符划分为若干个子字符串

17.6 字符串截取
17.7 字符串的不可变性

String 是一种不可变对象,字符串中的内容是不可改变的,字符串不可被修改,因为:

1. String 类在设计时就是不可改变的,String 类实现描述中已经说明了

  1. string 类中的字符实际保存在内部维护的 value 字符数组中,String 类被final修饰,表示该类不能被继承;

  2. value 被 final 修饰, 表明 value 自身的值不能改变,不能引用其他字符数组,但是其引用空间中的内容可以修改

  3. final修饰的变量不可变,final 修饰的引用变量,引用不可变

final 修饰的类表明该类不能被继承,final 修饰的引用类型表明该引用变量不能引用其他对象,但是其内容是可以被修改的,只是 String 类在设计时不暴露任何修改方法,所以才修改不了 value 的内容。

2. 所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象

比如 replace 方法:

17.8 字符串修改

尽量避免对 String 类进行修改,所有的修改创建新的对象,效率非常低下,在 Java 中可以使用 StringBuilder或StringBuffer来进行优化。

代码分析

java 复制代码
public static void main(String[] args) {
    String s = "hello";
    s += " world";        // 等价于 s = s + " world";
    System.out.println(s); // 输出:hello world
}

步骤1:字符串常量池与初始赋值

String s = "hello";

JVM 检查字符串常量池中是否存在值为"hello"的字符串,如果存在直接让 s 引用该对象,如果不存在,在常量池中创建"hello", 此时 s 指向常量池中的"hello"对象。

内存状态:

java 复制代码
s --> [String Pool] "hello"

步骤2:字符串拼接操作

s += "world" ;

执行过程:

  1. 创建临时 StringBuilder 对象(编译器优化),拼接字符串;
java 复制代码
new StringBuilder().append(s).append(" world").toString();
  1. 新字符串的储存:"hello world" 是动态生成的字符串,储存在堆内存中(不在常量池中,除非调用intern()方法,因为有 new 的存在,所以储存在堆里)

  2. 更新引用:s 的引用从常量池中的"hello" 变为堆中新的" hello world" 对象

内存状态变化:

java 复制代码
s --> [Heap] "hello world"
[String Pool] "hello" (未被修改,仍然存在)

步骤3:输出结果

17.9 StringBuilder和StringBuffer

为了方便字符串的修改, Java 中又提供 StringBuilder和StringBuffer类来增加效率

String、StringBuffer、StringBuilder的区别

  1. String 的内容不可修改,StringBuilder、StringBuffer的内容可以修改

  2. StringBuilder和StringBuffer大部分功能是相似的

  3. StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作

分析以下代码来清除 String 对象的创建(常量池中不存在新创建的字符串)

java 复制代码
String str = new String("ab");   // 会创建多少个对象
String str = new String("a") + new String("b");  // 会创建多少个对象

第一行 new String 就会在堆中创建一个,ab 字符串创建一个 String 对象;

第二行有两个 new String ,所以会在堆中创建两个对象,'a''b'在常量池会创建两个对象,StringBStringBuilder 创建的字符串'a+b'也就是'ab'创建一个对象,所以第二行会创建五个对象

=========================================================================

如果帮助到您的话希望能给个三连,祝您在以后的代码路上越走越远。

相关推荐
风中飘爻6 分钟前
JavaScript:BOM编程
开发语言·javascript·ecmascript
kyle~7 分钟前
ROS2---std_msgs基础消息包
开发语言·python·机器人·ros·机器人操作系统
满怀10158 分钟前
【NumPy科学计算引擎:从基础操作到高性能实践】
开发语言·python·numpy
我命由我123451 小时前
35.Java线程池(线程池概述、线程池的架构、线程池的种类与创建、线程池的底层原理、线程池的工作流程、线程池的拒绝策略、自定义线程池)
java·服务器·开发语言·jvm·后端·架构·java-ee
&zzz1 小时前
Python生成exe
开发语言·python
Chandler241 小时前
Go:方法
开发语言·c++·golang
CopyLower2 小时前
分布式ID生成方案的深度解析与Java实现
java·开发语言·分布式
随便@_@3 小时前
基于MATLAB/simulink的信号调制仿真--AM调制
开发语言·matlab·simulink·移动仿真
爱代码的小黄人3 小时前
深入解析系统频率响应:通过MATLAB模拟积分器对信号的稳态响应
开发语言·算法·matlab
vsropy3 小时前
matlab安装python API 出现Invalid version: ‘R2022a‘,
开发语言·python