Java基础知识复习笔记
本文是猫娘复习Java时所做的笔记,主要是为了巩固基础。
警告
本文不包含吉林大学软件学院Java程序设计课程中的GUI部分。
参考文献:【黑马程序员Java零基础视频教程_上部(Java入门,含斯坦福大学练习题+力扣算法题和大厂java面试题)】
试卷下载:百度网盘
Java相关概念
- JVM:Java Virtual Machine,Java虚拟机,负责解释和执行Java代码。
- JRE:Java Runtime Environment,Java运行时环境。包含JVM和核心类库。
- JDK:Java Development Kit,Java开发环境,包含JRE和开发工具。
- 安装Java环境需要在系统Path环境变量中添加相应路径。
HelloWorld
第一个Java程序:输出Hello, World语句
java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}HelloWorld是一个类,它可以创建对象。main是主函数,是程序入口。System.out是核心类库提供的标准输出对象,包含的println方法能够打印语句到控制台上,并换行。- Java源代码需要由
javac编译为.class字节码文件,再交由java执行,由JVM将字节码翻译为机器码执行。
输入输出
输入
- 利用
Scanner类
java
Scanner sc = new Scanner(System.in);
while (sc.hasNextInt()) {
System.out.println(sc.nextInt());
// 只要有整数就一直输出,直到读到不符合整数的字符串
// 同样支持double, byte, float, BigDecimal等类型
}
while (sc.hasNext()) {
System.out.println(sc.next());
// 只要有字符串就一直输出,以空格为分隔符,因此不能输出带空格的字符串
// 必须读到有效字符才开始输入,之前的空白字符会被去掉
}
while (sc.hasNextLine()) {
System.out.println(sc.nextLine());
// 按行读入字符串并输出
// 以回车为分隔符,可以得到带空格的字符串
}输出
printlnprint差别不大,只不过println自动添加换行符printf可实现与C/C++类似的格式化输出- 另外也可使用
String.format()方法生成格式化字符串,然后输出
数据类型
- Java共有8种基本数据类型:byte、short、int、long、char、boolean、float、double
- 其余均为引用类型,如数组、对象(包括基本数据类型的包装类)、字符串、枚举等
- 引用数据类型通常存放在堆内存中,基本数据类型存放在栈内存中
- 引用数据类型作为参数传递给函数时,传递的是这个引用,而基本数据类型传递的是该数据的副本,也就是传递到函数后,在该函数内部修改引用数据类型的值,会影响原对象的值
- 引用数据类型进行==操作,对比的是两个引用是否指向同一对象,而基本数据类型比较的是值是否相等
数组
- 数组是一种常见的容器,可以存储同种数据类型的多个值(需要考虑隐式转换)。
- 数组定义有两种格式,分别是
int[] array和int array[],推荐使用第一种。第二种格式是为了适配C/C++程序员的习惯。 - 静态初始化:
int[] array = new int[]{1, 1, 4, 5, 1, 4};可以简化为int[] array = {1, 1, 4, 5, 1, 4}; - 直接用
System.out.println()打印数组,只会得到该数组的地址值,格式为[I@0x11451419。[代表这是一个数组,I代表是int型数组。 - Java数组索引从0开始,获取数组中的元素,格式为
array[0]。为数组中的元素赋值,格式为array[4]=826 - 遍历数组有如下两种格式,一种是传统的for循环,另一种是foreach循环。
java
int[] arr = {1, 1, 4, 5, 1, 4};
String[] arr2 = {"1", "2", "3", "4"};
for(int i : arr){
System.out.println(i);
}
for(int i = 0; i < arr2.length; i++){
System.out.println(arr2[i]);
}- 动态初始化:
int[] array = new int[5],也就是不指定初始值,后续自行初始化。默认引用数据类型的初始值为null,数字类型为0,布尔为false,字符为\0 - Java数组的内存图与C++在堆区创建的数组类似。
- 二维数组定义:
int[][] array = {{1, 2, 3}, {4, 5, 6}};或者int[][] array = new int[2][3];访问与C++类似
方法
- 方法是程序的执行单元,重复的、具有独立功能的代码可以抽取到方法中。方法有利于提高程序的复用性和可维护性。
- 方法的定义:
java
public class Test {
public static int func1(int a){
// ...
return 0;
}
}- 方法需要在类内定义,且不允许嵌套定义。
public是访问控制修饰符,static表示静态方法(非静态方法不需要),int为返回值类型,func1为方法名称,int a为参数列表- 如不需要返回值,返回值类型为
void,return语句后不要跟返回值 - 名称相同,但是参数列表不同的两个方法构成重载,但仅有返回值不同而参数列表相同的不可以。重载示例:
java
public static int sum(int[] arr){
int sum = 0;
for(int i = 0; i < arr.length; i++){
sum += arr[i];
}
return sum;
}
public static double sum(double[] arr){
double sum = 0;
for(int i = 0; i < arr.length; i++){
sum += arr[i];
}
return sum;
}面向对象
类与对象
- 定义类
java
public class MyClass {
// 成员变量,又叫属性
public int a;
private double b;
protected String c;
// 成员方法
public static void main(String[] args) {
// ...
}
private int func1(int x) {
return x * x;
}
// 可能还包括:构造器、代码块、内部类等
}- 访问属性和方法与C++类似:
obj.aobj.func() - 获取一个类的对象,需要使用
new MyClass() - 建议:类名的首字母要大写,且要见名知义,采用驼峰命名。
- 一个.java文件中可以含有多个类,但是只能含有一个public类,且代码文件名必须与该public类相同。
- 类中对成员变量的定义可以赋给初始值,但也可以不给,存在默认值(与上文中数组的规则相同)。
封装
可以为变量和方法添加访问控制修饰符,控制外部对它们的访问。
private修饰的属性和方法只有本类方法能够直接访问,在外部不能直接被访问。
java
public class Student {
private String name;
private int age;
private String gender;
}
Student stu = new Student();
// stu.age = 9999; // 错误- 一般我们使用getter和setter方法访问私有成员,这样可以对这些成员的访问添加额外的控制。
java
public class Student {
private String name;
private int age;
private String gender;
public void setAge(int age) {
if(age > 50 || age < 0) {
System.out.println("Illegal Age!");
return;
}
this.age = age;
return;
}
public int getAge() {
return age;
}
}
Student stu = new Student();
// stu.age = 9999; // 错误构造方法
- 构造方法又叫构造器、构造函数,作用是在创建对象时为成员变量赋初始值。
- 之前我们使用
new Student()创建学生对象时,使用的是无参数的构造方法。 - 构造方法不能有返回值,也不需要加
void返回值类型,不可以编写return语句。构造方法的名称必须与类名完全相同,不能被手动调用,由JVM在创建对象时自动调用。
java
public class Student {
private String name;
private int age;
private String gender;
public Student() {
// 空参构造器,如果我们没有编写任何构造方法,会生成默认的空参构造方法。
// 如果已编写构造方法,则不会生成
this.name = "zhangsan";
this.age = 18;
this.gender = "unknown";
}
public Student(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
}
Student stu1 = new Student();
Student stu2 = new Student("lisi", 20, 'male');- 在IDEA中可以按快捷键Alt+Insert插入构造方法和getter setter。
this关键字
- 代表当前对象,与C++的
this指针类似。 - 每个非静态方法都会被隐式的传递一个
this参数,代表当前的对象,作为方法的第一个参数。 this也可用于在一个构造器中调用本类中其他的构造器,且必须在构造器的第一行。
静态成员
- 由该类所有对象共享的成员或者方法,可以将它设为静态成员。与C++不同的是,Java的静态成员可通过成员访问,也可通过类名访问。静态成员随着类的加载而被加载,先于所有对象存在。
- 静态成员属于类,而不属于任何一个对象。
- 静态方法常用于工具类和测试类中。
- 静态方法只能访问静态变量和静态方法,且没有this,因为它不属于任何一个对象,而是属于类本身。非静态方法可以访问所有静态/非静态的成员。
java
public class HelloWorld {
public static void main(String[] args) {
Student s1 = new Student(20, "lisi");
Student s2 = new Student(21, "wangwu");
Student s3 = new Student();
System.out.println(Student.getCount());
}
}
class Student {
private int age;
private String name;
private static int count = 0;
public Student(int age, String name) {
this.age = age;
this.name = name;
count++;
}
public Student() {
this(18, "zhangsan");
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static int getCount() {
return count;
}
}继承
- 继承适合描述具有共同特征,而在具体行为上又有差异的事物。Java中,继承需要使用关键字
extends,例如定义一个基于人类Person的学生类Student可以这样定义:public class Student extends Person。所有类的公共祖先类都是余胜军Object。 - 被继承的类称为父类(或者基类、超类),继承其他类的类叫做子类(或者派生类)。
- 使用继承,多个子类中实现相似功能的代码可以提取到父类中,无需重复实现,提高代码复用性。子类是父类的“一种特化”,可在父类的基础上增加更多行为、功能。
- Java不支持多继承,一个子类只能继承一个父类。但是,可以多层继承,例如C继承于B,而B又继承于A是可以的,B是C的直接父类,A叫做C的间接父类或者祖先类。
- 父类中
private私有的成员也会被子类继承,只不过子类无法直接访问。protected是保护成员,在本类以及自己的子类中可直接访问,但在类外不能直接访问。public是公共成员,类内外均可直接访问。如果未指定访问权限修饰符,则默认为包访问权限,在同一个包中的其他类中可见。 - 父类的成员可以通过
super关键字访问。 - 在Java中,非静态、非私有、没有被
final修饰的方法是虚方法,虚方法可被子类重写。建议在重写的方法上加上@override注解,以便发现重写中的错误。重写的方法,访问权限应该大于等于父类,以保证父类的引用可以访问这些方法;返回值类型应当小于等于父类(在类继承体系中)。
java
public class Main {
public static void main(String[] args) {
Animal a1 = new Cat("mimi", 3);
Animal a2 = new Dog("wangwang", 2);
a1.say();
a2.say();
}
}java
public class Animal {
String name;
int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public Animal() {
this(null, 0);
}
public void say() {
System.out.println("Hello " + name + " " + age);
}
}java
public class Cat extends Animal {
public Cat(String name, int age) {
super(name, age);
}
public Cat() {
this(null, 0);
}
@Override
public void say() {
System.out.println("meow " + name + " " + age);
}
}java
public class Dog extends Animal {
public Dog(String name, int age) {
super(name, age);
}
public Dog() {
this(null, 0);
}
@Override
public void say() {
System.out.println("woff " + name + " " + age);
}
}- 子类不会继承父类的构造方法,子类中所有的构造方法在默认情况下,会调用父类中的无参构造方法。子类构造方法的第一行应当为
super()调用父类的构造方法,如果我们在代码中没有加上,编译时也会生成。想要调用有参构造,必须在super()中指定参数。 - 使用
this()调用本类中其他构造方法时,不用super(),因为其他构造函数会调用。
多态
Java的多态与C++的基本上一样,都是通过父类指针调用不同的子类方法,产生不同的行为。可参考上面的动物类,初步了解Java多态的实现。
- 多态需要继承(或者实现)关系,要用父类引用指向子类对象,且需要子类重写父类的方法。
- 通过父类引用只能访问父类中包含的成员,子类自行增加的成员则不能通过父类引用访问。例如在上面的动物类中,假如
Cat类新增了一个catchMouse()方法,则Animal a = Cat(); a.catchMouse();会导致编译错误。 - 在程序运行时,实际上运行的是具体的子类的方法。
instanceof关键字可以判断一个对象是否是某个类的实例。如果想要调用某个子类的特有方法,但又不知道这个对象具体是什么类,就可以通过这个关键字判断(例如:if(a instanceof A){}),避免出现类型转换错误。
包
- Java中的包可以对各种类进行分门别类的管理,便于管理和使用。
- 一般情况下,包名采用公司域名反写,全英文+小写,需要见名知义。
- Java文件的第一行用
package com.example.mypackage;声明包名。 - 全类名/全限定名:一个类的完整标识,包括它所在的包名和自身的类名,可以唯一标识一个类,能够避免同名的类发生名称冲突。假如我们在
com.example.mypackage包中定义了Student类,则该类的全类名为com.example.mypackage.Student。 - 使用其他类时,最完整的类名实际上应该是全类名。但是全类名过于复杂,使用非常不方便,因此需要使用
import关键字导入这些类。- 同一个包中的其他类不需要导入。
java.lang中的包不需要导入。- 除上述两种情况之外,都需要导入包。
- 如果同时使用了来自不同包的同名的类,只能使用全类名。
final关键字
- final的字面意思,就是"最终"。在Java中,final可以修饰类、方法、变量。
- final修饰类时,表示该类是最终类,不能被继承。
- final修饰方法,该方法不能被子类重写。
- final修饰变量时,该变量成为常量,赋值后不能被修改。基本数据类型的常量,其值不能被修改;引用数据类型的常量,其指向的对象不能更换,但是仍然可以调用该对象的方法,修改该对象内部的数据。
权限修饰符
Java中,类的成员访问权限共有四种,分别是私有、默认、保护、公共。
| 修饰符 | 类内 | 同一个包内 | 其他包子类 | 不同包的无关类 |
|---|---|---|---|---|
private | ✅ | ❌ | ❌ | ❌ |
| 默认 | ✅ | ✅ | ❌ | ❌ |
protected | ✅ | ✅ | ✅ | ❌ |
public | ✅ | ✅ | ✅ | ✅ |
代码块
注意
如果您正在复习吉林大学相关课程的考试,您可以跳过本小节。
局部代码块
- 局部代码块是写在方法内的一对单独的大括号内的代码,其作用主要是提前结束变量的生命周期。
java
public class HelloWorld {
public static void main(String[] args) {
{
int a = 1;
System.out.println(a);
}
// 会导致编译错误
// System.out.println(a);
}
}构造代码块
- 构造代码块需要写在类成员的位置,在构造该类的对象时,会优先于构造方法执行。多个构造方法中重复的内容可以提取到构造代码块中。
java
public class HelloWorld {
public static void main(String[] args) {
Cat c1 = new Cat();
Cat c2 = new Cat();
}
}
class Cat {
{
System.out.println("Meow~");
}
Cat() {
System.out.println("喵~");
}
}
// Meow~
// 喵~
// Meow~
// 喵~静态代码块
- 静态代码块是通过
static关键字修饰的构造代码块,随着类的加载而执行,自动触发,而且只会被执行一次。 - 一般在类加载时,需要进行数据初始化时使用。
java
public class HelloWorld {
public static void main(String[] args) {
Cat c1 = new Cat();
Cat c2 = new Cat();
}
}
class Cat {
static {
System.out.println("Meow~");
}
Cat() {
System.out.println("喵~");
}
}
// Meow~
// 喵~
// 喵~抽象类与抽象方法
- 如果在父类中抽取了各个子类的共性方法,但是不能确定该父类的具体方法体,这时候就可以使用抽象类和抽象方法。
- 如果一个类中定义了抽象方法,则必须声明为抽象类。与C++不同的是,抽象类必须用
abstract关键字声明。抽象类和抽象方法的定义如下所述:
java
public class HelloWorld {
public static void main(String[] args) {
Animal cat = new Cat();
Animal dog = new Dog();
cat.say();
dog.say();
}
}
abstract class Animal {
public abstract void say();
}
class Cat extends Animal {
@Override
public void say() {
System.out.println("Meow");
}
}
class Dog extends Animal {
@Override
public void say() {
System.out.println("Woff");
}
}- 抽象方法的定义不能有方法体。
- 抽象类不能实例化,也就是说不能创建抽象类的对象。
- 抽象类不一定有抽象方法,但有抽象方法的类必须是抽象类。
- 抽象类可以有构造方法,创建子类对象时用于给成员变量赋值。
- 抽象类的子类必须重写所有的抽象方法,或者为抽象类。因此抽象方法不得定义为
private,因为这样的方法无法被子类重写。
接口
- 相较于抽象类,接口是对行为的抽象,更偏向对方法定义的约定。
- 接口用关键字
interface定义,接口和类之间是实现关系,用关键字implements表示。 - 接口不能被实例化。接口的实现类必须重写接口中的所有方法,或者为抽象类。
- 一个类可以实现多个接口,可以在继承一个抽象类的同时实现多个接口。
- 与类一样,
public修饰的接口,其所在的文件名必须与该接口名称相同。
java
public class HelloWorld {
public static void main(String[] args) {
CatchMouse cm = new Cat();
cm.catchMouse();
}
}
abstract class Animal {
public abstract void say();
}
interface CatchMouse {
void catchMouse();
}
class Cat extends Animal implements CatchMouse {
@Override
public void say() {
System.out.println("Meow");
}
@Override
public void catchMouse() {
System.out.println("Caught 1 mouse");
}
}
class Dog extends Animal {
@Override
public void say() {
System.out.println("Woff");
}
}- 接口可以定义成员变量,但只能是静态常量,默认会添加
public static final修饰。成员方法默认为public abstract关键字修饰,也就是抽象方法。由于接口没有继承,只有实现,因此接口的成员都是公共的。 - 接口没有构造方法。
- 如果一个类继承了多个接口,这些接口有重名的方法,则只需要实现一次即可。通过哪个接口的引用来调用该方法,调用的都是子类中重写的这个方法。
- 接口和接口之间可以多继承。
- JDK8加入了接口的默认方法,可以给接口中的方法编写方法体,主要是为了更新接口时更方便,需要使用
default关键字,例如public default void fun(){}。默认方法不是抽象方法,可以不重写;但是如果实现了多个接口,而这些接口中有同名的默认方法,则必须重写。实现类重写接口中的默认方法时,应当去掉default关键字。 - JDK9可以在接口中加入私有方法,可抽取各个默认方法中的重复代码。
- 适配器模式:如果一个接口中的方法太多,而我们并不需要全部的方法,可以设计一个中间的适配器类,实现所有方法(但都是空方法,这个类一般声明为抽象类),再编写我们需要的类,继承这个适配器类,然后只重写需要的方法即可。
内部类
注意
内部类在吉大软院期末考察较少。如果时间紧迫,建议跳过。
类的成员包括属性、方法、构造方法、代码块、内部类。内部类就是在一个类的内部再定义一个类,例如在A类的内部定义了B类,则B类称为内部类。一般情况下,内部类表示的事物是外部类的一部分,且内部类单独出现无意义,这时候我们会考虑使用内部类。
- 内部类可以直接访问外部类的成员,包括其私有属性。
- 外部类想要访问内部类的成员,则必须创建对象。
- Java16前不支持内部类的静态成员。
成员内部类
写在类的成员部分的内部类,就是成员内部类。
- 成员内部类可被访问控制修饰符修饰,与方法、变量的效果类似。
- 获取内部类的对象有两种方法:
- 所在的外部类编写方法,对外提供内部类的对象。
- 直接创建。
java
public class HelloWorld {
public static void main(String[] args) {
// 直接创建
Car.Engine ce = new Car("高级轿车", "黑色", 24)
.new Engine("田所发动机", 24);
ce.show();
}
}
class Car {
private String carName;
private String carColor;
private int carAge;
public void show() {
System.out.println("Car Name: " + carName);
System.out.println("Car Color: " + carColor);
System.out.println("Car Age: " + carAge);
}
class Engine {
private String engineName;
private int engineAge;
public void show() {
Car.this.show(); // 外部类this
System.out.println(carName + " " + carColor + " " + engineName + " " + engineAge);
}
public Engine(String engineName, int engineAge) {
// 内部类this正常发挥作用
this.engineName = engineName;
this.engineAge = engineAge;
}
}
private Engine engine;
public Car(String carName, String carColor, int carAge) {
this.carName = carName;
this.carColor = carColor;
this.carAge = carAge;
// 外部类中可以直接创建
this.engine = new Engine(carName, carAge);
}
public Engine getEngine() {
return engine;
}
}
// Car Name: 高级轿车
// Car Color: 黑色
// Car Age: 24
// 高级轿车 黑色 田所发动机 24- 内部类想要访问外部类的方法、属性,可以通过
Outer.this(Outer需要替换为实际上的外部类名)调用。Java会为内部类隐式生成一个外部类的引用,指向其对应的外部类。
静态内部类
静态内部类是一种特殊的成员内部类,就是用static修饰的成员内部类。
- 与静态方法类似,静态内部类也只能访问外部类的静态成员。如果需要访问外部类的非静态成员,需要创建对象。创建静态内部类的对象的格式为:
Outer.Inner oi = new Outer.Inner() - 可以直接调用静态内部类的静态方法,例如
Outer.Inner.s_fun();
java
public class HelloWorld {
public static void main(String[] args) {
Outer.Inner oi = new Outer.Inner();
oi.fun();
Outer.Inner.s_fun();
}
}
class Outer {
public static class Inner {
public static void s_fun() {
System.out.println("Static Inner Function");
}
public void fun() {
System.out.println("Inner Function");
}
}
public void o_fun() {
Inner inner = new Inner();
inner.fun();
System.out.println("Outer Function");
}
}
// Inner Function
// Static Inner Function局部内部类
定义在方法内部的内部类叫做局部内部类,类似于局部变量。
- 外界无法直接使用该内部类,必须在定义该内部类的方法内部创建该内部类的对象并使用。
- 局部内部类可以直接访问外部类的成员,也可以访问方法内的局部变量。
java
public class HelloWorld {
public static void main(String[] args) {
Outer outer = new Outer();
outer.o_fun();
}
}
class Outer {
public void o_fun() {
System.out.println("Outer o_fun");
class Inner {
public void i_fun() {
System.out.println("Inner i_fun");
}
}
Inner inner = new Inner();
inner.i_fun();
}
}
// Outer o_fun
// Inner i_fun匿名内部类
匿名内部类是没有标识符的内部类,在四种内部类中最为重要。
- 匿名内部类的定义格式可参考如下代码:
java
public class HelloWorld {
public static void main(String[] args) {
new CatchMouse() {
@Override
public void catchMouse() {
System.out.println("猫抓老鼠");
}
}.catchMouse();
}
}
interface CatchMouse {
public abstract void catchMouse();
}- 可以看到,创建的匿名内部类在定义时移除了
class关键字和类的名称。该类实现了CatchMouse接口,重写了该接口中的所有方法,接口名写在大括号之前,就表示该匿名内部类实现的是该接口。匿名内部类就是一个类的子类或接口的实现类。 - 匿名内部类的定义就是
new CatchMouse()之后大括号中的内容,new关键字创建的是这个匿名内部类的对象,而不是接口CatchMouse的实例。 - 匿名内部类的定义、创建对象中,我们可以看到三种要素:
- 实现/继承关系:实现一个接口,或者继承其他类。
- 重写方法:重写所有抽象方法。
- 创建对象:创建了匿名内部类的对象。
- 匿名内部类可以写在方法内,也可以写在类的成员位置。
- 当方法的参数需要传递接口或者类时,但该实现类只需要使用一次,如果直接编写实现类过于麻烦,我们就可以用匿名内部类简化代码。
- 匿名内部类对象如果想多次使用,可以把该匿名内部类的对象传递给一个被继承/实现的类/接口的引用。
字符串
- 字符串是
java.lang包中提供的类String,无需导入。 - 字符串创建后,其值不可修改。
- 使用
String s1 = "abc";这样形式创建的字符串,abc常量存储在常量池中,如果再创建一个String s2 = "abc";,则s1与s2指向的数组是同一个。 - 使用
byte[] b = new byte[]{'a', 'b', 'c'}; String s3 = new String(b);创建的字符串,每次初始化都会拷贝该字符数组。String s4 = new String(b);s3与s4指向的字符数组不是同一个。
java
public class StringFromConstant {
public static void main(String[] args) {
String s1 = "114514";
String s2 = "114514";
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);
}
}
// 输出:114514 114514 truejava
public class StringFromConstructor {
public static void main(String[] args) {
byte[] b = new byte[]{97, 98};
String s1 = new String(b);
String s2 = new String(b);
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);
}
}
// 输出:ab ab false- 如需比较两个字符串的值,可以用
equals(考虑大小写)或者equalsIgnoreCase(忽略大小写)方法,返回值均为布尔型。
java
public class HelloWorld {
public static void main(String[] args) {
String s1 = "114514abc";
String s2 = "114514Abc";
String s3 = "114514abc";
System.out.println(s1.equals(s2));
System.out.println(s1.equals(s3));
System.out.println(s2.equalsIgnoreCase(s3));
}
}
// 输出:false true true- Java8之后,如果几个常量字符串拼接,编译时会提前优化。例如
String s1 = "abc"; String s2 = "a"+"b"+"c";中,两个字符串对象指向常量池中的同一个字符串。
StringBuilder
注意
如果您正在复习吉林大学相关课程的考试,您可以跳过本小节。
StringBuilder可以看作一种容器,创建之后其中的内容是可变的,能够提高对字符串的操作效率。- StringBuilder有两种常用的构造方法。
- 空参构造:创建一个空白的可变字符串对象,不含内容。
- 传入字符串对象:根据传入的字符串的内容,创建可变字符串对象,内容与该字符串相同。
- 常用方法包括
append(拼接)reverse(反转)等:
java
public class HelloWorld {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("123");
System.out.println(sb); // 123
StringBuilder sb2 = sb.append("456");
sb.append("456");
System.out.println(sb); // 123456
System.out.println(sb.length()); // 6
System.out.println(sb.reverse()); // 654321
System.out.println(sb.toString()); // 654321
System.out.println(sb == sb2);
}
}- 空的StringBuilder默认创建长度为16的数组,当超出该限制需要扩容时,扩展到原限制的2倍+2;仍然不足时扩展到实际长度。
StringJoiner
注意
如果您正在复习吉林大学相关课程的考试,您可以跳过本小节。
- StringJoiner是JDK8加入的特性,可以在构造字符串时添加分隔符、前缀、后缀(可以只传入分隔符)。
java
public class HelloWorld {
public static void main(String[] args) {
int[] arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
StringJoiner sj = new StringJoiner(", ", "[", "]");
for (int i = 0; i < arr.length; i++) {
sj.add(String.valueOf(arr[i]));
}
System.out.println(sj);
}
}
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]常用容器
注意
如果您正在复习吉林大学相关课程的考试,您可以跳过本小节。
ArrayList
- ArrayList使用了泛型,创建一个
ArrayList需要传入一个类型参数。注意,基本类型是不能传递给泛型参数的,我们需要传递对应的包装类。 ArrayList在java.utils包中,需要导入。- 常用方法:
add:添加一个元素remove:传入整数时,为删除对应索引的元素;传入对象时,删除相应的元素(删除第一个遇到的,若不存在则不操作)。例如下面的代码中,直接传递0则会删除索引为0的元素,而传入包装类Integer类的0,则会直接删除元素0。get:得到对应索引值的元素。set:设置对应索引值的元素。indexOf:传入一个对象,查找该对象第一次出现的位置。
java
public class HelloWorld {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 10; i++) {
list.add(random.nextInt(10));
}
System.out.println(list);
list.remove(0); // 按照索引移除
// list.remove((Integer) 0); // 按照元素移除
// list.remove(list.indexOf(0)); // 按照元素移除
System.out.println(list);
list.set(0, 826);
System.out.println(list);
System.out.println(list.get(0));
}
}
// [1, 1, 6, 3, 3, 0, 1, 5, 0, 8]
// [1, 6, 3, 3, 0, 1, 5, 0, 8]
// [826, 6, 3, 3, 0, 1, 5, 0, 8]
// 826