目录
[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 中数据类型主要分两类: 基本数据类型和引用数据类型
基本数据类型有 四类: 整形,浮点型,字符型,布尔型
八种:字节型,短整型,整形,长整型,单精度浮点型,双精度浮点型,字符型,布尔型。
不论是16位系统还是32位系统,int 都只占用4个字节,long 都占8个字节;
整形和浮点型都是带有符号的;
整形默认为 int,浮点默认为 double;
引用数据类型分三种:类,接口,数组, 值得注意的是数组是定长的,除非重新初始化否则是不能改变的。
Java 中引用类型在堆里,基本类型在栈里。基本数据创建的类型称为基本变量,该变量空间存放的是其所对应的值,而引用数据类型则存的是对象所在的空间的地址
3. 变量
3.1 变量概念: 在 JAVA 程序中经常改变的内容,变量使用前必须要赋初值,否则会报错。
注意事项:
整形变量赋值不能超过该变量表示范围,否则会导致溢出。
变量使用前必需赋值,但是成员变量使用前可以不赋值,原因是当对象被创建时,系统会自动赋予默认值。
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 定义的代码块,一般用于初始化静态成员变量
静态代码块不管生成多少个对象都只会执行一次;
静态成员变量是类的属性,在 JVM 加载类时申请空间并初始化:
实例代码块只有在创建对象时才会执行;
执行顺序:静态代码块>实例代码块>构造方法
10. 内部类
内部类概念:可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。
10.1 实例内部类
实例内部类:没有被 static 修饰的成员内部类
在实例内部类中,可以任意访问外部类中的成员;
如果外部类和实例内部类中具有相同名称成员时,优先访问内部类的;
如果要让问同名称外部类成员, 必须: 外部类名称.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. 继承
有以下注意点:
访问的成员变量子类中有, =优先访问自己的成员变量;
访问的成员变量子类中无, 则访问父类继承下来的, 如果父类也没有定义, 则编译报错;
如果子类父类成员变量同名, 优先访问自己的;
访问子类父类方法优先级同上, 当明确传参时, 优先匹配参数类型合理的方法
子类父类代码块执行顺序如下:
父类静态块>子类静态块>父类实例代码块>父类构造方法>子类实例代码块>子类构造方法
final 修饰的变量或者字段不能被修改,修饰类则此类不能被继承
12. 向上转型和向下转型
12.1 向上转型
向上转型是就是创建一个子类对象,将其当成父类对象来使用。向上转型为了代码实现更简单灵活,但是不能调用到子类特有的方法。
12.2 向下转型
父类引用指向子类对象,可以调用子类特有的方法。因为有可能出现ClassCastException的风险,所以必须用instanceof检查。
13. 抽象类
注意事项:
抽象类不能直接实例化对象;
抽象发放不能是private;
抽象方法不能被 final 和 static 修饰, 因为抽象方法需要被子类重写;
抽象类被继承时必须要重写父类中的抽象方法;
抽象类中不一定包含抽象方法, 但是有抽象方法的类一定是抽象类;
抽象类可以有构造方法,供子类创建对象时初始化父类的成员变量;
抽象类无需抽象方法的情况可能包含:代码服用、限制实例化、预留拓展性
14. 接口
注意事项:
接口类型是一种引用类型,但是不能直接 new 接口对象;
接口中的每一个方法都会被隐性指定为 public abstract;
接口中的方法不能再接口中实现, 只能由实现类来实现;
重写接口中方法时不能使用默认的访问权限;
接口中可以有变量, 但是会被指定为 public static final 变量
接口中不能有静态代码和构造方法
如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类
接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
jdk8中:接口中还可以包含default方法。
在 Java 中, 类和类之间是单继承, 一个类只能有一个父类, 但是一个类可以实现多个接口, 接口与接口之间可以多继承。即:用接口可以达到多继承的目的。
15. 接口与抽象类区别与联系
-
抽象类可以有构造方法、具体方法、成员变量、和抽象方法;但是在Java8之前, 接口都只有抽象方法和常量,Java8之后可以有默认方法和静态方法,但是不能有成员变量。
-
抽象类只能单继承, 接口可以多继承。抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中 不能包含普通方法, 子类必须重写所有的抽象方法.
-
使用场景上抽象类适合定义一些共同方法,但是希望由子类实现;而接口适合定义行为契约,不关心具体实现,允许多个不同类实现同一个接口,增加灵活性。 另一方面,接口由于多继承,适合在大型项目中定义模块之间的交互契约,而抽象类适合在具有层次接口的类之间共享代码。

抽象类 是 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方法。
需要注意的是:
用户创建的类(包括抽象类)无论是否显式继承其他类,最终都会直接或间接继承Object;
接口不继承 Object 类, 他们的父接口只能是其它接口;(比如,定义一个接口A,它不会自动拥有Object的方法,比如toString()或equals(),除非显式声明或者通过其他方式。
实现接口的类仍然继承 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 类型;
-
先按照字典序比较大小,如果出现不等字符,直接返回大小差值
-
如果前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 类实现描述中已经说明了


-
string 类中的字符实际保存在内部维护的 value 字符数组中,String 类被final修饰,表示该类不能被继承;
-
value 被 final 修饰, 表明 value 自身的值不能改变,不能引用其他字符数组,但是其引用空间中的内容可以修改
-
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" ;
执行过程:
- 创建临时 StringBuilder 对象(编译器优化),拼接字符串;
java
new StringBuilder().append(s).append(" world").toString();
-
新字符串的储存:"hello world" 是动态生成的字符串,储存在堆内存中(不在常量池中,除非调用intern()方法,因为有 new 的存在,所以储存在堆里)
-
更新引用:s 的引用从常量池中的"hello" 变为堆中新的" hello world" 对象
内存状态变化:
java
s --> [Heap] "hello world"
[String Pool] "hello" (未被修改,仍然存在)
步骤3:输出结果
17.9 StringBuilder和StringBuffer
为了方便字符串的修改, Java 中又提供 StringBuilder和StringBuffer类来增加效率

String、StringBuffer、StringBuilder的区别
-
String 的内容不可修改,StringBuilder、StringBuffer的内容可以修改
-
StringBuilder和StringBuffer大部分功能是相似的
-
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'创建一个对象,所以第二行会创建五个对象
=========================================================================
如果帮助到您的话希望能给个三连,祝您在以后的代码路上越走越远。