JAVA SE

Posted by zangxin on September 29, 2024

JAVA SE

1.java跨平台原理

image-20240928140844473

2.功能最小单元-方法

3.注释、字面量、变量、关键字、标识符

注释:// // //***/, javac编译后去掉注释,与执行无关

字面量:程序中能直接书写的数据,”hello”, 1, 3.14, ‘A’, true, false, null,\t,\n

1
2
3
4
5
6
7
8
// 定义整型变量i1,并初始化为二进制值101010
int i1 = 0b101010;

// 定义整型变量i2,并初始化为十六进制值fa
int i2 = 0xfa;

// 定义整型变量i3,并初始化为八进制值12
int i3 = 012;

变量:代表内存地址,

变量类型:

数据类型 默认值 大小
boolean false 不确定
char ‘\u0000’ 2 字节
byte 0 1 字节
short 0 2 字节
int 0 4 字节
long 0L 8 字节
float 0.0f 4 字节
double 0.0 8 字节

整数字面量默认使用int类型

浮点字面量默认使用double类型

关键字:java内置的语法修饰词

标识符:自定义的名称,用于变量名,类名、方法名

一般由数字字母下划线和$组成

不能以数字开头,不能是关键字,不能包含一些特殊字符(%,&,#,…)

变量名:小驼峰

类名:大驼峰

4.方法(method/function)

方法是功能的基本单元

格式:修饰符 返回值 方法名(参数列表){

​ // 方法体

​ return 返回值;

}

1
2
3
4
// 定义一个方法,求两个数的和
public static int getSum(int a, int b) {
    return a + b;
}

方法是否需要接受数据?

方法是否需要返回数据?无返回值,返回void

重载方法:一个类中,出现多个方法名称相同,但是他们的形参列表不同,那么这些方法就称为重载方法。

System.out.println()就是重载方法

return; 用于无返回值的方法中提前结束方法

5.类型转换

自动类型转换:类型范围小的变量,可以直接赋值给类型范围大的

byte–>short–>int–>long–>float–>double

char–>int

强制类型转换:大–>小

类型范围大数据类型不能直接转换成小的类型,因为可能造成窄化时数据损失,需要使用强制类型转换符

1
2
3
4
int b = 12;
byte j = (byte) b;// 强制转换
print3(j);
public static void print3(byte a) {}

表达式的自动类型转换:在表达式中,小范围类型变量会自动转换成表达式中大范围的类型,再参与运算。

byte、short、char–>int–>long–>float–>double

表达式结果最终类型是由最高类型决定

byte、short、char是直接转换成int类型参与运算的。

byte + byte的结果是int

6.输入/输出

输入:调用Scanner的API

1
2
3
4
5
6
Scanner scanner = new Scanner(System.in);
System.out.println("请输入年龄:");
int age = scanner.nextInt();
System.out.println("请输入姓名:");
String name = scanner.next();
System.out.println("年龄:" + age + ",姓名:" + name);

7.运算符

+ - * / %

+: 加;连接。能加就加,不能加就连接

++/–:自增运算符

赋值运算符

= += -= *= /= %=

1
2
3
4
byte b1 = 10;
byte b2 =  20;
b1 += b2; // 等价于 b1 = (byte) (b1 + b2) +=自带强制类型转换
System.out.println(b1);

关系运算符:

> < >= <= == !=

运算的结果为boolean值

三元运算符

boolean表达式 ? 为true时执行的语句 : false时执行的语句

1
String result = a % 2 == 0 ? "偶数" : "奇数";

逻辑运算符

与:& or &&(短路与,左边为false, 就不执行右边的表达式了)

或: or   (短路或,左边为true,就不执行右边的表达式)

非:!

异或:^

8.程序控制流程

分支结构 if-else 和switch-case 根据条件真假,决定执行不同代码

if的三种形式

1
2
3
if(condition) {}
if(condition) {} else {} // 非此即彼
if(condition) {} else if(condition2) {} [else]

switch: 通过比较值是否相等,来决定执行那条分支

if在功能上比switch强,if可以进行区间判断

switch表达式类型只能是byte short int char 枚举 String 不支持float double(小数,java这些类型不精确),long

case给出的值不允许重复,且只能是字面量(常量),不能是变量。

正常使用switch时,一定要写break,否则会出现穿透现象。

合理利用穿透可以简少冗于代码

循环结构

for

while

功能与for一致

知道 循环次数用for,不知道用while

do-while 先执行后判断,至少执行一次

死循环

1
2
3
while (true) {}
for (; ; ) {}
do{} while(true);

continue: 跳过本次循环

break: 结束循环

9、数组

程序中数据存储:变量只能存储一个变量

数组:是一个数据容器,可以用来存储一批同 类型的数据

静态初始化数组

1
2
3
4
// 定义一个数组,存储10个名字
// 静态初始化数组,定义时已经确定数组的长度
String[] names = {"张三", "李四"};
String[] names2 = new String[]{"张三", "李四"};

动态初始化数组

1
2
3
// 数组动态初始化
// 只确定数组的类型和长度,不指定具体的值
double[] scores = new double[8]; // 声明后默认值是0.0

动态初始化数组的默认值规则

byte short char int long: 0

float double: 0.0

boolean: false

引用类型:类,接口,数组,String:null

访问数组中的元素

1
2
3
// 随机获取一个索引值
int index = (int) (Math.random() * names.length);
String name = names2[index];

数组的遍历:求和,搜索,求最值

打乱顺序(洗牌)

1
2
3
4
5
6
7
8
for (int i = 0; i < pokers.length; i++) {
    // 生成一个随机索引,范围在0到扑克牌数组长度之间
    int index = (int) (Math.random() * pokers.length);
    // 交换:将当前元素与随机索引处的元素交换位置。
    String temp = pokers[i];
    pokers[i] = pokers[index];
    pokers[index] = temp;
}

二维数组:

二维数组的元素是一维数组

1
2
3
4
5
6
7
8
9
10
11
// 静态初始化
// 动态初始化
int[][] arr2 = new int[2][3];
arr2[0] = new int[]{1, 2, 3};
arr2[1] = new int[]{4, 5, 6};
// 遍历
for (int i = 0; i < arr.length; i++) {
    for (int j = 0; j < arr[i].length; j++) {
        System.out.print(arr[i][j] + " ");
    }
}

二维数组洗牌

1
2
3
4
5
6
7
8
9
10
for (int i = 0; i < arr.length; i++) {
    for (int j = 0; j < arr[i].length; j++) {
        int r = (int) (Math.random() * arr.length);
        int c = (int) (Math.random() * arr[i].length);
        // 交换:将当前元素与随机索引处的元素交换位置
        int temp = arr[r][c];
        arr[r][c] = arr[i][j];
        arr[i][j] = temp;
    }
}

10.面向对象编程

对象:一种特殊的数据结构:成员变量(状态)+方法(行为)

设计对象:1.设计对象的模板类,2通过new关键字获取具体的对象

jvm中对象 :划分为栈内存、堆内存、方法区

image-20241004223155220

万物皆对象,谁的数据谁存储

构造器:特殊的方法:没有返回值,方法名和类名相同

创建对象时,对象会去调用构造器

1
Student student = new Student();

作用:创建对象时,同时完成对象成员变量的初始化赋值

类默认就自带一个无参构造器,但如果自定义了有参构造器则该无参构造器将被覆盖,如果还想使用无参构造器,则必须显式声明。

this关键字:指代当前对象,哪个对象调用这个方法,this就拿到哪个对象

可以用于解决成员变量名字与类中方法局部变量名冲突问题: this.variable_name = variable_name

封装:类就是一种封装

封装设计要求:合理隐藏,合理暴露

1.合理隐藏:private 关键字修饰成员变量,就只能在本类中被直接访问,其他地方不能访问。

2.合理暴露:使用public修饰的getter和setter方法合理访问成员变量的取值和赋值

javabean: 类中成员变量全部私有,并提供public修饰的getter和setter方法,提供无参构造器

实体类的应用:实体类的对象只负责数据存储,而对数据的业务处理交给其他类对象来完成,以实现数据和操作的分离

static关键字:可以修饰成员变量、成员方法

静态变量:有static 修饰的成员变量,属于类,只有一份,会被类的所有对象共享

实例变量:无static修饰,属于具体对象私有,每个对象都有一份

image-20241005111906477

静态变量的应用场景:如果数据只需要一份,且希望能够被共享(访问,修改),则该数据可以定义成静态变量来记住。

记录某类创建了多少次,可以把静态变量count的自增写在构造器中

静态方法(函数):static修饰的成员方法,属于类

实例方法:无static修饰的成员方法,属于对象。

什么时候使用静态方法:如果这个方法只是为了做一个功能,并且不需要直接访问对象的数据,这个方法直接定义成静态方法。如果这个方法是对象的行为,需要访问对象的数据,这个方法必须定义成实例方法。

静态方法的常用场景

工具类 设计:xxxUtil,私有化构造器,静态方法。

注意事项: 静态方法可以访问静态变量,不能访问实例变量。

实例方法可以访问静态变量,也可以访问实例变量。

实例方法中可以出现this关键字,静态方法中不可以出现。

继承

提高代码复用性

extends关键字:建立两个类之间的继承关系

子类能继承父类的非私有成员(成员变量、方法)

继承后对象的创建:f子类的对象是由子类、父类共同完成的。

权限修饰符:限制类中成员(成员变量,方法,构造器)能够被访问的范围。

private(只能本类)< default(本类和同包下) < protected(本类,同包,子孙类中) < public(任意位置)

继承特点:

java是单继承:一个类只能继承一个直接父类,java不支持多继承,但支持多层继承,Java中所有类都是Ojbect类的子类,就近原则:优先访问自己类中,自己类中没有才会访问父类

如果想访问父类中和子类中同名的成员,可以使用super.成员

方法重写:子类写了一个方法,形参列表与父类方法一样的覆盖了父类的方法,子类调用该方法,而不是原始的父类方法。

规范:声明不变,重新实现。

note: 子类重写父类方法时,访问权限大于或者等于父类,不能造成父类方法可以调用,子类重写的方法不能调用

重写方法的返回值类型,必须与被重写的父类方法的返回值类型一样,或者是其子类

私有方法(私有方法不能被继承,所以不能被重写),静态方法不能被重写。

子类构造器:子类的 全部构造器,都会调用父类的构造器,再执行自己。

默认情况下,子类的全部构造器的第一行代码都是super(),写不写都有,它会调用父类的无参数构造器。

如果父类没有无参构造器,则我们必须在子类构造器的第一行手写super(…),指定去调用父类的有参构造器。

image-20241005182444671

super(…)调用父类的有参构造器的目的:为对象中包含父类这部分的成员变量进行赋值。

this(…)调用兄弟构造器:代码复用

this和super都只能放在第一行,所以二者不能同时出现在第一行。

多态

多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态。

成员方法:编译看左边,运行看右边

成员变量:编译看左边,运行看左边

多态的前提:有继承/实现关系,存在父类引用子类对象,存在方法重写

多态的注意事项:多态是对象、行为的多态,java中的属性(成员变量)没有多态

多态的好处:在多态形式下,右边的对象是解耦合的,更便于扩展和维护;

定义方法时,使用父类类型的形参,可以接受一切子类对象,扩展性更强,更便利。

1
Animal a =  new Dog();

多态的缺点:多态不能调用子类独有的功能,要解决此问题,需要向下转型(强制类型转换)

多态下的类型转换

1
2
3
4
5
6
// 自动类型转换
Animal a =  new Dog();
// 强制类型转换
Dog d = (Dog) a;
// 乌龟才有的游泳方法
d.swiming();

在强制转换前,先用instanceof判断是否为要转的类型,避免出现运行时ClassCastException

final关键字:可以修饰类、方法、变量。

修饰类:类不能被继承了。

修饰方法:方法不能被重写乐。

修饰变量:该变量只能被赋值一次。

常量:final修饰的静态变量,常用于系统的配置信息。

代码可读性更好,可维护性也更好.

程序编译后,常量会被“宏替换”:出现常量 的地方全部会被替换成字面量,这样保证了使用常量和直接使用字面量的性能是一样的。

note:final修饰基本数据类型变量,变量存储的数值不能改变

final修饰的引用类型变量,变量存储的地址不能被改变,但是地址所指向对象的内容是可以改变的

单例类(设计模式):作用:确保某个类只能创建一个对象。

实现步骤:1.私有构造器,定义一个类变量记住类的一个对象,定义一个类方法,返回对象。(饿汉式)

1
2
3
4
5
6
7
public class A {
    private A() {}
    private static A instance = new A();
    public static A getInstance() {
        return instance;
    }
}

懒汉式:用的时候,才开始创建对象。步骤:私有构造器,定义一个静态变量存储对象,提供一个静态方法,保证返回的是同一个对象。

1
2
3
4
5
6
7
8
9
10
public class B {
    private static B instance;
    private B(){}
    public static B getInstance() {
        if (instance == null) {
            instance = new B();
        }
        return instance;
    }
}

枚举类

1
2
3
public enum A {
    X,Y,Z;
}

反编译后代码:

1
2
3
4
5
6
7
8
public final class A extends java.lang.Enum<A> {
  public static final A X;
  public static final A Y;
  public static final A Z;
  public static A[] values();
  public static A valueOf(java.lang.String);
  static {};
}

枚举类都是最终类,不能被继承,枚举类的父类是java.lang.Enum

枚举类的第一行只能罗列一些名称,这些名称都是变量,并且每个变量都会记住枚举类的一个对象

枚举类的构造器都是私有的,因此枚举类对外不能创建对象

枚举类很适合做信息分类和标志

抽象类

abstract关键字:修饰类就是抽象类,修饰方法就是抽象方法

抽象类中不一定要有抽象方法,但是抽象方法一定在抽象类中。

抽象类和普通类一样可以有:成员变量,成员方法,构造器。

抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现方法。

有得有失:得到了抽象方法的能力,失去了创建对象的能力,

抽象类的使命就是被子类继承。

一个类继承抽象类,必须重写所有的抽象方法或者该类也定义为抽象类 。

使用抽象类的 好处:强制要倪去实现抽象的方法。感觉有点多余,就是为了多加一个抽象方法。

模板方法设计模式:提供一个方法作为完成某个功能的模板,模板方法封装了每个实现步骤,但允许子类提供特定步骤的实现:

1.定义一个抽象类

2.在里面定义2个方法:一个是模板方法:把共同的步骤放到里面去。一个是抽象方法:不确定的实现步骤,交给具体的子类来完成。

建议:用final来修饰模板方法。

1
2
3
4
5
6
7
8
9
public abstract class People {
    public final void write() {
        System.out.println("固定流程1");
        System.out.println("固定流程2");
        writeMain();
        System.out.println("固定流程3");
    }
    public abstract void writeMain();
}

接口:

interface关键字:定义接口 。

jdk8之前接口中只可以写:常量(static final)、抽象方法。

接口不能创建对象。

接口中的成员默认是public的

接口是用来被实现(implements)的,实现接口的类被称为实现类,一个 类可以同时实现多个接口。

借口的好处:弥补了类单继承的不足,一个类可以实现多个借口,使类的角色更多,功能更强大;让程序可以面向接口编程,更利于程序的解耦合

jdk8之后接口新增了三种形式的方法(子类不用强制实现):增强了接口的能力

默认方法:使用default修饰,使用实现类的对象调用

1
2
3
default void go() {
    System.out.println("========");
}

私有方法(jdk9):只能在借口内部被调用

1
2
3
private void run() {
    System.out.println("runnnnnnnnnnnnnnnn...");
}

类/静态方法:必须用接口名.方法名调用

1
2
3
static void show() {
    System.out.println("show...");
}

接口的注意事项:

1.接口与接口可以多继承:一个接口可以同时继承多个接口

类与类:单继承,一个类只能继承一个直接父类

类与接口:多实现,一个类可以实现多个接口

2.一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承,也不支持多实现。

1
2
3
4
5
6
7
8
9
interface A{
    void go();
}
interface B{
    String go();
}
interface C extends A,B{

}

image-20241007175220593

3.一个类继承了父类,同时又实现了接口,如果父类和接口中有同名的方法,实现类会优先使用父类的方法

4.一个类实现了多个接口,如果多个接口中存在同名的默认的方法,可以不冲突,只要这个类重写该方法即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface A{
    default void go() {
        System.out.println("A go");
    }
}
class Father {
    void go() {
        System.out.println("Father go");
    }
}
class Son extends Father implements A{
    @Override
    public void go() {
        A.super.go(); // 在默认继承父类go方法的情况下,调用父接口的方法
        super.go();
    }
}

抽象类、接口的区别和对比

相同点:都是抽象形式,都可以有抽象方法,都不能创建对象。

都是派生子类形式:抽象类是被子类继承使用,接口是被实现类实现

都能支持多态

不同点:抽象类中可以定义普通类的全部普通成员,接口只能定义常量,抽象方法

抽象类只能被单继承,接口可以被类多实现

一个类继承抽象类就不能再继承其他类,一个类可以实现多个接口

抽象类体现模板思想,更利于做父类 ,实现代码的复用性 最佳实践

接口更适合做功能的解耦合 最佳实践

代码块:是类的五大成分之一(成员变量、构造器、方法、代码块、内部类)

代码块分为静态代码块、实例代码块。

静态代码块:类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次。

作用:完成类的初始化,如:对静态变量的初始化赋值。

实例代码块:

特点:每次创建对象时,执行实例代码块,并在构造器前执行。

作用:和构造器一样,都是用来完成对象的初始化的。如:对实例变量进行初始化赋值。

内部类:如果一个类定义在另一个类内部,这个类就是内部类。

成员内部类:就是类的一个普通成员

1
Outer.Inner inner = new Outer().new Inner();

可以直接访问外部类的实例成员、静态成员

可以拿到当前外部类对象,格式:外部类名.this

静态内部类:有static修饰的内部类,属于外部类自己持有。

1
Outer.inner inner = new Outer.inner();

可以直接访问外部类的静态成员,不能直接访问外部类的实例成员

局部内部类:是定义在方法中,代码块中,构造器中等…..

匿名内部类:是一种特殊的局部内部类;匿名:不需要为这个类命名,默认有个隐藏的名字

匿名内部类本质就是一个子类,并立即创建出一个子类对象,匿名内部类实际上是有名字的:外部类名$编号数字.class

1
2
3
4
5
6
Animal a = new Animal() {
    @Override
    public void cary() {
        System.out.println("我是一个匿名内部类");
    }
};

反编译后:

1
2
3
4
5
6
7
8
class Test$1 extends Animal {
    Test$1() {
    }

    public void cary() {
        System.out.println("我是一个匿名内部类");
    }
}

作用:用于更方便的创建一个子类对象

常见形式:通常作为一个对象参数传输给方法。

函数式编程:可以Lambda函数替代某些匿名内部类对象,从而让程序更简洁

lambda表达式:jdk8新增的语法形式,表示函数。

(被重写的方法形参列表) -> { 被重写的方法的方法体}

lambda只能替代函数式接口的匿名内部类

函数式接口:只有一个抽象方法的接口,可以加上@FunctionalInterface标记

方法引用:

静态方法引用:类名::静态方法,在 -> 前后的参数形式一致时可以使用方法应用

实例方法引用:对象名::实例方法,与静态方法类似

特定方法引用:

1
2
3
4
5
6
// 目标:特定类型的方法引用
// 需求:有一个字符串数组,里面有一些人员名,要求按照姓名的首字母
String names[] = {"Tom", "Jerry", "Lucy", "Mary", "Alice","曹操", "andy", "chris" };
Arrays.sort(names, (s1, s2) -> s1.compareToIgnoreCase(s2));
Arrays.sort(names, String::compareToIgnoreCase);
System.out.println(Arrays.toString(names));

构造器引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
    // CarFactor cf = name -> new Car(name);
    CarFactor cf = Car::new; // 构造器引用

    Car c1 = cf.getCar("奔驰");

}

interface CarFactor {
    Car getCar(String name);
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class Car {
    private String name;
}

String API

String创建字符串的对象的方式

方式一: String name = “abc”

方式二:构造器

image-20241008112944469

二者的区别:

只要是以第一种”hello”的方式创建的字符串对象放在常量池中,相同内容只放一个

1
2
3
4
5
6
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true

String s3 = new String("hello");
System.out.println(s1 == s3); // false

image-20241008114434449

String 常用方法

image-20241008114648444

比较相等时使用equals方法,不要使用==方法,==比较的是地址,==为true表示是同一个对象

集合:集合是一种容器,用来装数据的,类似于数组

为什么使用集合?数组大小固定,集合长度可变。

ArrayList:创建对象,增、删、改、查

image-20241008120418804

GUI编程

GUI:Graphical User Interface,图形用户界面。

java的gui包

awt(Abstract window tookit) 提供了一组原生的gui组件,依赖于操作系统的本地窗口系统

swing:基于awt,提供了更丰富的gui组件,轻量级组件,不依赖于本地窗口系统

常用的Swing组件:

JFrame:窗口

JPanel:用于组织其他组件的容器

JButton: 按钮组件

JTextField: 输入框

JTable:表格

布局管理器(Layout Manager)

FlowLayout:水平布局管理器

BorderLayout:东南西北中

GridLayout:网格布局管理器

BoxLayout:盒子布局管理器

常用的事件监听器

点击事件监听器 ActionListener

按键事件监听器 KeyListener

事件的几种常见的写法: 1.提供实现类,2直接使用匿名内部类的对象,3自定义窗口