java基础语法

基础语法

堆,栈,常量池,内容存放的位置等

DOS命令

image-20220112111838470

一些小函数

getClass()

查看运行类型

StringBuffer

类似vector的不定长度的String

length() 实际长度

capacity() 理论容量

String a; String[] res = a.split(";");

String.substring(a,b)

[a,b) 截取字符串子串

Arrys.sort()

Math.round()

四舍五入的求整数

返回类型为long int a = (int) Math.round(x);

Math.abs()

Math.random()

Math.random() 	//随机double类型浮点数 (0.0 ~ 1.0)

Random类

Random r = new Random();
int randomNum = r.nextInt(3); //随机生成[0,3)的一个随机整数

instanceof

用来判断一个对象是否是一个类或该类子类的实例

注意判断的是运行类型

class A{}
class B extends A{}

B b = new B();
A a = nwe B();
A aa = new A();
//判断的是运行类型
(aa instanceof B) == false;
(b instanceof B) == true;
(b instanceof A) == true;
(a instanceof B) == true;
(a instanceof A) == true;

高精度类型

整形高精度 BigInteger 、浮点高精度 BigDecimal

基础数据类型转换成高精度可以用 valueOf()

在对这些高精度进行运算的时候不能使用 + 、-、*、/

而是运用对应的方法 add()、substract()、multiply()、divide()、mod()

判断是否相等用 compareTo()

包引入 like 头文件

import java.util.Scanner;

基础变量类型的不同

float

java的浮点数默认是double 定义float(要加f或者F) 如下: float test = 1.1f;

bool && boolean

cpp 是 bool java是 boolean

注意java中不能用int代替bool

比如 int a = 1;if(a){}; 会报错:int类型不能用作bool量

自动类型转换

当进行多种数据类型的混合运算时,会转换成容量最大的类型

从小到大为 : char -> int -> long -> float -> double && byte -> short -> int

byte short char 不会互相转换而是直接转成int类型

同样的,大容量向低容量转换可能会损失精度

对Sring类型的转换

注意cpp 的 string s小写 java 的 String S大写

基础数据类型转换成String

int a = 123; String b = a + "";

拓展:toString() 函数

string 类型对其他基础数据类型的转换

String s = "123";

int num1 = Integer.parseInt(s);
float num2 = Float.parseFloat(s);
double num3 = Double.parseDouble(s);

//对char的转换
s.charAt(0);	//得到字符串的第一个字符,以此类推

逻辑运算符

^ 异或

| 逻辑或 (与 || 的不同 不管第一个是否为 true 都会判断第二个 效率慢)

& 逻辑与 (与 && 的不同 不管第一个是否为 false 都会判断第二个 效率慢)

没有 <<< , 且 >>> 用0填充高位,>>用符号位填充高位

java的输入

  1. nextLine() 读入一行
  2. next() 读入下一个单词
  3. nextInt() 读入下一个int
  4. nextDouble() 读入下一个double
  5. hasNext() 是否还有下一个输入,返回Boolean
  6. hasNextInt()
  7. hasNextDouble()

for_each

int[] a = {2,1,3,5,4};

for(int i : a) System.out.println(i);

跳转控制语句break

一般和goto一样,不在开发中使用,但作为一个完整的知识点还是需要了解。

label1:
for(int i = 1;i <= 10;i ++){
    System.out.println("i = " + i);
    label2:
    for(int j = 1;j <= 10;j ++){
        System.out.println("j = " + j);

        if(j == 4){
            System.out.println("break");
            break label1;
        }
    }
}
System.out.println("label * " + i);

数组

数组的初始化

最好是写成 int[] a 这种类型,可以更清楚的说明该变量是数组类型

变量类型 + [] 数组长度可以用数组名.length来得到

允许存在匿名数组 new int[] = {1,2,3};

int[] a 和 int a[] 都可以

int n;
Scanner cin = new Scanner(System.in);
n = cin.nextInt();

int a[] = new int[n];	//动态数组
int b[] = {1,2,3};		//初始化数组

b = new int[] {2,3,4};	//匿名数组赋值,不用创建新变量

for(int i = 0;i < n;i ++) a[i] = cin.nextInt();

//可以先定义后开大小
int c[]; 

for(int i = 0;i < n;i ++){
    if(i == 2){
        c = new int[i];
        for(int j = 0;j < i;j ++) c[i] = cin.nextInt();
    }
}

数组的赋值

类指针的指向、引用操作

//类似指针,不是数组拷贝
int a[] = {1,2,3};

//这种类型的赋值是引用赋值,当 c 改变时 a 也会改变,cpp不能采用此种赋值
int c[];
c = a;
for(int i = 0;i < c.length;i ++) System.out.println(c[i]);

c[0] = 66;
for(int i = 0;i < c.length;i ++) System.out.println(a[i]);

数组的拷贝

int a[] = {1,2,3};
int b[] = new int[a.length];

for(int i = 0;i < a.length;i ++) b[i] = a[i];

二维数组

int a[][] 或 int[] a[] 或 int[][] a
//静态初始化
int [][] a = {{1,2,3},{4,5,6},{7,8,9}};

for(int i = 0;i < a.length;i ++)            //有几个a[i]
    for(int j = 0;j < a[i].length;j ++)     //a[i]有几个元素
        System.out.println(a[i][j]);

//动态定义行和列
Scanner cin = new Scanner(System.in);
        
int a[][] = new int[3][3];
for(int i = 0;i < 3;i ++)
    for(int j = 0;j < 3;j ++)
        a[i][j] = cin.nextInt();

//只能动态定义列而不是行
Scanner cin = new Scanner(System.in);

int a[][] = new int[3][];

for(int i = 0;i < 3;i ++){
    int temp = cin.nextInt();
    a[i] = new int[temp];
    for(int j = 0;j < temp;j ++) a[i][j] = cin.nextInt();
}

for(int i = 0;i < 3;i ++) {
    for (int j = 0; j < a[i].length; j++) {
        System.out.print(a[i][j] + " ");
    }
    System.out.println();
}

//引用也类似
int a[][] = {{1,2},{3,4}};
int b[][] = a;

b[1][1] = 99;

for(int i = 0;i < 2;i ++)
    for(int j = 0;j < 2;j ++)
        System.out.println(a[i][j]);

面向对象

jvm的内存对象存在形式

栈 -> 堆 -> 基础数据类型就直接放在堆中,否则放在堆中存首地址、信息存方法池中的常量池

image-20220111153531572

类的和方法

类和方法的构造

实例

import java.util.Scanner;

public class t1 {
    public static void main(String[] srg){
        Scanner cin = new Scanner(System.in);

        System.out.println("Please input a && b :");
        int a = cin.nextInt(),b = cin.nextInt();

        fun xixi = new fun();
        xixi.build(a,b);

        xixi.show();
    }
}

class tool{
    public void print(int map[][]){
        for(int i = 0;i < map.length;i ++)
            for(int j = 0;j < map[i].length;j ++)
                System.out.print(map[i][j] + " " + (j == map[i].length - 1 ? "\n" : ""));
    }
}

class fun{
    Scanner cin = new Scanner(System.in);
    int arr[][];

    public void build(int a,int b){
        arr = new int[a][b];
        for(int i = 0;i < a;i ++)
            for(int j = 0;j < b;j ++)
                arr[i][j] = cin.nextInt();

        System.out.println("Build success");
    }

    public void show(){
        for(int i = 0;i < arr.length;i ++) {
            for (int j = 0; j < arr[i].length; j++) {
                System.out.print(arr[i][j] + " ");
            }
            System.out.println();
        }

        tool temp = new tool();
        temp.print(arr);
    }
}

传参问题(要分清是 基本数据类型 还是 引用)

注意,方法传基础数据类型在方法中的改变不会反馈到main函数里面,但是传数组会,因为数组传的是地址

同理,传入一个main中的对象,在方法中对该对象进行属性的改变,也会反馈到main中

public class try{
    public static void main(String[] srg){
        a xixi = new a();
        xixi.test = 1;

        int[] xixixi = {1,2};

        int xixixixi = 1;

        fun hello = new fun();
        hello.fun1(xixi);
        System.out.println("After change xixi is " + xixi.test);

        hello.fun2(xixixi);
        System.out.println("After change xixixi is " + xixixi[0]);

        hello.fun3(xixixixi);
        System.out.println("After change xixixixi is " + xixixixi);

        hello.fun4(xixi);
        System.out.println(xixi.test);
    }
}

class a{
    int test;
}

class fun{
    public void fun1(a GG){
        GG.test = 66;
    }

    public void fun2(int[] a){
        a[0] = 66;
    }

    public void fun3(int a){
        a = 66;
    }
    //实参原本和main中的xixi同指向一个地址,如果改变具体数值会有影响
    // 但如fun4一样指向null不会对main有影响
    public void fun4(a GG){
        GG = null;
    }
}

重载

  1. 方法名要相同
  2. 参数类型或个数或顺序至少一个不同
  3. 返回类型无要求
class fun{
     public void cout(int n){
         System.out.println(n);
     }

     public void cout(double n){
         System.out.println(n);
     }
}

可变参数

传参长度不固定,本质是一个数组

可变参数实参可直接为数组

当可变参数和普通参数一起放在形参列表,要保证可变参数在最末尾

一个形参列表里只能有一个可变参数

public class t1{
    public static void main(String[] srg){
        Scanner cin = new Scanner(System.in);
        int n1 = cin.nextInt();
        int n2 = cin.nextInt();
        int n3 = cin.nextInt();

        fun a = new fun();
        System.out.println(a.fun1(n1,n2,n3));
    }
}

class fun{
    public int fun1(int... num){
        int sum = 0;
        for(int i = 0;i < num.length;i ++) sum += num[i];
        return sum;
    }
}

作用域

属性和局部变量可以重名,访问时遵循就近原则

构造器(构造函数)

完成对象的初始化,new的时候自动调用

可以重载以实现不同的传参初始化

public class t1 {
    public static void main(String[] srg){
        //test、test2都是对象引用
        person test = new person(1,"hah");
        person test2 = new person();
    }
}

class person{
    //注意直接对属性的初始化会在构造器前执行
    int age = 90;
    String name;
    //没定义构造方法的时候的默认构造器,如果有构造方法时要重新声明,不然无参会报错
    public person(){}
    
    public person(int a,String b){
        age = a;
        name = b;
    }
}

this关键字

一个指向当前对象的指针

在那个对象里就代表哪个对象

class fun{
    int a;

    public void fun1(int n){
        this.a = n;
    }
}

this(参数列表)可以访问构造器,只能在构造器中使用

并且要在第一行

class fun{
    fun(){
        this(1,"hah");
        System.out.println("this()要在第一行");
    }

    fun(int a,String b){}
}

命名规范和使用

命名规则一般是com.公司名.项目名.业务模块名

类似文件夹,起到分区的作用

使用不同包的class的时候可以带包名然后使用如 有com.test1 && com.test2.fun1

import com.test1.fun1;

public class test {
    public static void main(String[] args) {
        fun1 fun1 = new fun1();		//要么导入然后不带包名
        com.test2.fun1 fun11 = new com.test2.fun1();	//要么带包名引用
    }
}

package

package com.test2;	//作用是声明当前类所在的包,放在最上面,一个类最多只有一个package

import java.util.Scanner;	//impor在package后,在类定义前,可以多句

public class fun1 {}

访问修饰符

级别 同类 同包 子类 不同包
private × × ×
默认 × ×
protected ×
public

封装

意义是隐藏实现细节、对数据进行验证等

How to

  1. 属性设为private
  2. 提供一个public的方法进行set
  3. 提供一个public的方法进行get
class person{
    public String name;
    private int age;
    private int salary;
    
    person(){}
    //构造器也能遵守规则
    person(int age,int salary,String name){set(age,salary,name);}
    
    public void set(int age,int salary,String name){
        if(age < 0 || age > 120) return;
        if(name.length() < 2 || name.length() > 6) return;

        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public void show(){
        System.out.println(name + " " + age + " " + salary);
    }
}

继承

父类 == 基类 == 超类 --------- 子类 == 派生类

父子关系是相对的

子类继承了所有属性和方法、但private类型不能被子类直接访问

提高了复用性、扩展性、维护性

语法 && 实例

class 子类 extends 父类

实例:com.learn.extend_

more detail

  1. 子类的构造器必须调用父类的构造器,对父类进行初始化,默认调用父类的无参构造器
  2. 若父类不存在无参构造器,则必须用super指定构造器
  3. super构造器需要放在构造器的第一行(super只能在构造器中使用)
  4. 不能同时使用(super() 父类构造器 和 this() 自身构造器),因为二者都必须是第一句
  5. 所有的类都是object的子类image-20220113110723888

ctrl + H 可以查看层级关系

  1. 单继承机制(与cpp区别),一个子类只能有一个直接父类

super

可以通过super实现对父类属性和方法的访问

属性和方法同理,下面详细说方法

如果不重名 super.a() == this.a() == a()

否则只能通过 super.a() 来访问父类方法

对 a() 这种形式的调用,先找本类,有就调用,没有再找父类,再父类的父类直到Object,若找到了但是无法访问,报错,若没找到,提示方法不存在

快捷键 ctrl + B 可以快速访问方法所在的类

super也可以访问更上一级的属性和方法

比如 A -> B -> C

A.a() B.b() C.a()

在C中 super.a() 访问到的是 A.a()

遵循就近原则

不能在子类构造器第一行以外的地方调用super()构造器

实例com.learn.super_

super和this的比较

区别 this super
访问属性 先找本类,没有再找父类 直接找父类
访问方法 先找本类,没有再找父类 直接找父类
调用构造器 本类构造器,必须放在首行 父类构造器,必须放在子类构造器首行
特殊 表示当前对象 子类中访问父类对象

方法的重写/覆盖(override)

子类的方法和父类一样(名称、参数),子类返回类型一样或者返回类型是父类返回类型的子类,子类的方法会覆盖父类的方法

子类方法不能缩小父类方法的权限

如 : protected -> private ×

但 : protected -> public √

实例:com.learn.override_

多态

前提是存在继承关系

重载和重写就体现了多态、

相当与用父类接受子类并当子类使用

当Dog和Cat是Animal的子类的时候

public void feed(Animal animal){} 可以传入Dog类或者Cat类正常使用

对象的多态

  1. 一个对象的编译类型和运行类型可以不一样
  2. 编译类型在编译的时候就确定了,无法改变
  3. 运行类型是可以变化的
  4. 编译类型看等号的左边,运行类型看等号的右边

Animal animal = new Dog(); //编译类型 Animal 运行类型 Dog

animal = new Cat(); //编译类型 Animal 运行类型 Cat

向上转型

语法 : 父类类型 引用名 = new 子类类型

A -> B -> C;

A a = new C();

B b = new C();

父类编译类型,子类运行类型

父类指父亲、爷爷、曾爷爷...

不能调用子类的特有成员

在编译阶段还是属于父类

但是在运行的时候调用的就是子类成员了

编译阶段由编译类型决定,运行阶段由运行类型决定

向下转型

语法 : 子类类型 引用名 = (子类类型) 父类引用

Animal -> Cat

要求父类引用必须指向强转对象

Animal animal = new Animal();
animal = new Dog();
Dog dog1 = (Dog) animal;
animal = new Cat();
//Dog dog1 = (Dog) animal;  //animal当前指向Cat,不能转成Dog
Cat cat1 = (Cat) animal;    //转换过后此时的animal & cat1 都是 Cat 的引用

属性重写问题

属性没有重写一说,值和编译类型有关

下面输出为 1

package com.learn.PolyMethod_;

public class AboutNumber {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.number);
    }
}

class A{
    int number = 1;
}

class B extends A{
    int number = 2;
}

动态绑定机制 (重要)Dynamic_binding

调用方法的时候,方法会和对象的内存地址/运行类型绑定

调用属性的时候,没有动态绑定机制,那里声明哪里使用

多态数组

数组的定义类型为父类类型,保存的实际元素为子类类型

Objec类详解

1. equals

equals 和 == 的区别

对引用来说 -> == 判断的是地址

对基础数据类型 -> == 判断内容,也可以看作判断地址,相同常量在常量池中位置相同

基础数据类型是存放在常量池里的,如 int a = 1,b = 1,a == b

对不是基础数据类型的数据来说,内容相同不一定地址相同

深入了解

为什么是判断内容

默认是判断地址是否相等,但是子类往往会对其进行重写,使其进行内容判断

不确定的话可以ctrl + b查看源码

在实际使用中可以自己重写 com.learn.equals_

如 String、Integer

2. hashCode

根据地址得来的hash编码,是一个int

3.toString

默认返回:全类名 + @ + 哈希值的十六进制

如 :com.learn.toString_.Monster@776ec8df

子类往往重写toString方法来返回对象的属性信息(快捷键重写)

打印或拼接对象的时候调用

直接输出的时候默认调用 toString 方法 -> sout(Monster) == sout(Monster a.toString())

4.finalize

该对象的生命周期结束(比如赋值n ull),在销毁对象前自动调用 (析构函数?

该方法已经被弃用了...

断点调试(DEBUG)

属于运行状态,可以看作正常运行,判断依据是运行类型

快捷键

  1. F7 跳入
    1. F8 跳过 / 逐行执行
  2. shift + F8 跳出
  3. F9 resume 执行到下一个断点

static关键字

声明为静态

对象方法声明为静态可以通过 类名.方法() 直接调用

如 People.say();

在类中声明一个 静态变量 ( 类变量 )

这个变量是这个类的所有对象共享的

class A{
    public static int count = 0;
}

A a1 = new A();
A a2 = new A();

a1.count ++,a2.count ++:

//count == 2

类变量可以通过类名访问,如

A.count ++;

java设计者推荐访问方式

  • 当然,与类变量相似的,也可以定义类方法。

静态方法的好处是在使用的时候可以直接通过类名调用,而不用建立一个实例化对象

这样的话在一些工具类里声明静态方法,用起来就比较方便

注意

  1. 类方法中没有 this & super 具体原理参考 jvm 八股文

  2. 静态方法只能访问静态变量或者静态方法,而普通方法的访问没有限制

main方法详解

main方法是 JVM 调用的

并且 JVM 调用时不同包、也不创建对象,所以修饰符必须是 public + static

String[] args 有什么作用

image-20220117200902810

用于接受文件名后的字符,看作一个字符串数组

传入的数据是执行程序时 执行的程序 后面的字符串

java 执行的程序 参数1 参数2 参数3

main 是该类中的 static 成员,可以调用静态成员和参数,遵循 static相关的规则

代码块

[修饰符]{ 代码 };

修饰符一般只有 static 可选

又称为初始化块,属于类中的成员、是类的一部分,类似于方法,将逻辑语句封装在方法体中,通过{} 包围起来。

与类方法不同的是,没有方法名、没有返回、没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用

看作构造器的补充

class Movie{
    public String name;
    public double price;

    public Movie(String name) {
        this.name = name;
        System.out.println("Only name");
    }

    public Movie(String name, double price) {
        this.name = name;
        this.price = price;
    }

    //代码块的优先级永远高于构造器,改代码运行会先运行代码块
    {
        System.out.println("这是代码块");
        System.out.println("不管调用哪个构造器都会调用这个代码块");
    }
}

image-20220117203940764

细节

  1. static代码块也叫做静态代码块,作用就是对类进行初始化

    不管 new 多少新对象,该代码块只会在第一次加载类的时候运行

static {
        System.out.println("这是代码块");
        System.out.println("不管调用哪个构造器都会调用这个代码块");
    }
  1. 什么时候类会被加载

    • 创建对象实例的时候 ( new )
    • 创建子类对象实例,父类也会被加载
    • 使用该类的静态成员的时候 ( 包括属性和方法 )
  2. 普通代码块在创建对象实例的时候会被调用,创建一次就调用一次,如果使用该类的静态成员,普通代码块与静态代码块不同,不会执行

  3. 创建一个对象时的调用顺序

    1. static 代码块和属性 并按定义顺序

    2. 普通代码块和属性 并按定义顺序

    3. 构造方法

  4. 构造器隐含的语句,而static型此时早已初始化完毕

class A{
    public A{
        //super();
        //代码块;
        ---------------
        构造器内容
    }
}
  1. 继承时的顺序

    1. 父类 static
    2. 子类 static
    3. 父类普通
    4. 父类构造
    5. 子类普通
    6. 子类构造
  2. 静态代码块只能直接调用静态成员,普通代码块可以调用所有

单例设计模式

饿汉式(SingleTon01_)

目的:使一个类只创建一个对象

类加载的时候就被创建,和是否使用没有关系(static 关键字作用)

  1. 将构造器私有化
  2. 类的内部直接创建
  3. 提供一个公共的static方法用于返回在类的内部直接创建的对象

线程安全但是可能存在浪费资源的问题

懒汉式(SingleTon02_)

防止饿汉式创建不使用造成空间浪费

  1. 构造器私有化
  2. 定义一个 static 对象
  3. 提供一个 public 方法返回对象

不会造成资源浪费

但是存在线程安全问题

final关键字 ( 有点像 const ? )

java 中没有 const 通过 static final 来实现常量的定义

什么时候会用到final关键字

  1. 当不希望类被继承的时候,可以用 final 修饰
  2. 当不希望父类的方法被子类改变时
  3. 当不希望类的某个属性可以被修改
  4. 当不希望某个局部变量被修改

注意事项和使用细节

  1. final 修饰的属性又叫常量,一般用XX_XX_XX_XX 命名,如:TAX_RATE
  2. final 修饰的属性在定义的时候必须赋初值,确定后不能再修改
    1. 定义时直接等号赋值
    2. 构造器中赋值
    3. 代码块中赋值
  3. 如果是 static final 初始化位置只能是
    1. 定义时直接等号赋值
    2. 静态代码块中赋值
  4. final 类不能继承,但可以实例化对象
  5. 一个类不是 final 类但是含有 final 方法,该方法不能重写但是可以被继承
  6. 一般来说,一个类如果是 final 的,就没必要再对方法进行 final 修饰
  7. static final 搭配使用的时候不会导致类加载,底层编译器进行了优化
  8. final 不能修饰构造器
  9. 包装类 (Integer , Double , Float , Boolean ) 都是 final,String 也是

抽象类

当父类方法不确定的时候,考虑将其设为抽象类

抽象方法就是没有实现的方法,即没有方法体

如果定义抽象方法,则该类必须为抽象类

一般来说,抽象类会被继承,由其子类来实现抽象方法

如:public abstract void eat(); //没有方法体

细节

  1. 抽象类不能被实例化
  2. 抽象类不一定要包含抽象方法,但包含抽象方法一定得是抽象类
  3. abstract 只能修饰类和方法,不能修饰属性和其他的
  4. 如果一个类继承了抽象类,则它必须实现抽象类的抽象方法、除非自己也声明为抽象类
  5. 抽象方法不能用 final、static、private 修饰,这些关键字都是和重写相违背的,共用会报错

抽象模板模式 P401

接口

接口就是给出一些没有实现的方法,封装到一起,到某个类要用的时候再具体实现

定义 :interface B{}

使用方式 :class A implements B {}

A 实现 B 接口

B 是一个接口,A类中必须实现B中定义的方法

JDK 7.0 前的接口没有方法体,8.0 以后可以定义默认方法(default修饰)、静态方法

在接口中,抽象方法可以省略 abstract 形容词

细节

  1. 接口不能实例化
  2. 接口中所有的方法是 public ,接口中的抽象方法可以不用 abstract 修饰
  3. 一个普通类实现接口,就必须将该接口的所有方法实现
  4. 抽象类实现方法可以不用实现接口的方法
  5. 一个类可以实现多个接口 -> class A implements B,C
  6. 接口中所有的属性是 final ,对 int a 实际上是 public static final int a; 所以可以 接口名.属性名
  7. 接口不能继承类,但可以继承其他的接口
  8. 接口的修饰符只能是 public 和 默认,这点和类的修饰符是一样的

和继承的区别

接口主要是把接口的功能在当前类上实现

继承主要是把父类的功能在当前类上实现

继承主要是解决代码的可维护性和复用性

接口主要是解决设计规范问题并且接口更为灵活

接口的多态特性

com.learn.Interface_

接口类型的变量可以指向实现了该接口的对象实例

与继承的多态相似

父类类型的变量可以指向子类的对象实例

与继承的多态一样,实现特殊的方法需要向下转型后调用

多态传递

interface A{}
interface B extends A{}
class C implements B{}
//相当与爷爷指向孙子,多态传递
A a = new C();

内部类

一个类(A)的内部又完整的嵌套了另一个类(B),A 就叫外部类,B就叫内部类

内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系

类的五大成员

属性、方法、代码块、构造器、内部类

分类

定义在外部类的局部位置上(一般在方法内)

1. 局部内部类(有类名)
  1. 可以直接访问外部类的所有成员,包含私有的
  2. 不能添加访问修饰符,它的地位就是一个局部变量,只能用 final 修饰
  3. 作用域:定义它的方法、代码块中
  4. 外部类调用内部类 -> new 然后调用
  5. 它本质还是一个类、但地位是一个局部变量,只能在定义域内使用
  6. 与外部类的成员重名时要调用外部类成员 使用 :外部类名.this.成员(如:Outer.this.same)
2. 匿名内部类(没有类名、重点) Anonymous
  1. 该类没有名字(底层分配有名字,但表面是没有的)、同时它还是一个对象
  2. 具体看 com.learn.InnnerClass_.AnonymousInnerClass_
  3. 格式: new 类或接口(){}
  4. 可以不接收直接调用

定义在外部类的成员位置上

1. 成员内部类
  1. 定义在外部类的成员位置、可直接访问外部类的所有成员
  2. 可以添加任意访问修饰符,因为它的地位就是一个成员
  3. 外部类调用内部类 -> new 然后调用
  4. 重名调用外部 name && Outer.name
2. 静态内部类(static)
  1. 只能访问静态成员,不能访问非静态成员
  2. 外部类调用内部类 -> new 然后调用
  3. 重名调用外部 name && Outer.name

异常 ( Exception )

快捷键 crtl + alt + t

e.getMessage()	//得到简单错误信息

Error 和 异常 不同,是虚拟机无法处理的严重问题,如栈溢出、Out of memory等,程序会崩溃

异常分为 运行时异常 和 编译时异常

运行时异常

  1. 空指针使用异常 (Test01)
  2. 数学运算异常 (Test02)
  3. 数组越界 (Test03)
  4. 类型转换异常 (Test04)
  5. 数字格式不正确异常 (Test05)

编译时异常

对于编译异常,程序中必须处理,try - catch 或者 finally

image-20220124123315053

异常处理

1) try - catch - finally

程序员自己处理

异常发生时,系统会把捕获到的异常封装成一个 EXception 对象 传入 catch 的 Exception e 中

得到 Exception 后自行处理,没有异常则不会执行 catch

发生异常后会跳入 catch 不会执行剩下的代码

finally 始终会被执行,通常放入释放资源的代码

细节
  1. 可以有多个 catch ,必须是子类异常在前,只会进入一个 catch 语句
  2. 也可以 try - finally ,但这样相当于没有捕获异常,出现异常会造成程序崩溃

2) throws

将异常抛出,交给调用者处理,最顶级的处理者就是 JVM ( 不断抛给上一个调用者 )

如 JVM -> main -> f1 -> f2(抛出异常)

则 f2 -> f1 -> main -> JVM

//除 JVM 的可以选择 try - catch -finally

//JVM 是直接输出异常信息,然后终止程序

细节
  1. 对于程序中出现的异常,没有特殊处理默认就是throws
  2. 在 throws 过程中如果有 try - catch 处理就不用继续 throws
  3. 子类重写父类的方法时,子类重写的方法抛出异常类型要么和父类一样,要么是子类

自定义异常

定义一个类,继承 Exception (编译异常)或者 RuntimeException(运行异常)

com.learn.Exception_.CustomException_

throw 和 throws 的区别

意义 位置 后面跟的
throws 异常处理的一种方式 方法声明处 异常类型
throw 手动生成异常对象的关键字 方法体中 异常对象

枚举(enumeration)

枚举是一组常量的组合,它属于一种特殊的类,里面只包含一组有限的特定对象

实现方式

自定义类

使用 enum 关键字

com.learn.enum_

注意事项

  1. 当我们使用 enum 关键字创建一个枚举类的时候,默认继承 Enum 类,并且是一个 final 类

所以可以使用 Enum类 的方法

  1. 使用无参构造器创建枚举对象的时候,小括号可以省略 如 ( SPRING; )
  2. 创建多个枚举对象的时候使用 间隔,并在结尾加上
  3. 枚举对象必须放在枚举类的行首

Enum类的方法

  1. toString :已经重写过了,返回的是当前对象名 ,子类可以重写
  2. name : 返回当前对象名,子类不能重写
  3. ordinal :返回当前对象的位置号,默认从 0 开始
  4. values :返回当前枚举类中所有的常量 (返回的是一个数组,包含所有的枚举对象)
  5. valueOf :将字符串转化成枚举对象,要求字符串必须为已有的常量名,否则报异常
  6. compareTo :比较两个枚举常量,比较的就是位置号

细节

  1. enum 隐式继承了 Enum 类了,不能再继承其他类
  2. 枚举类可以使用接口

注解

注解也被成为元数据,用于修饰数据星信息

和注释一样,注解也不影响程序逻辑,但注解可以被编译和运行,相当于嵌入在代码中的补充信息

三个基本注解

  1. @Override :限定某个方法,是重写父类方法,该注解只能用于方法

  2. @Deprecated :用于表示某个程序(类、方法等)已过时

    过时并不代表不能用而是不推荐使用,还是可以正常使用的

  3. @SuppressWarnings :抑制编译器警告

    用法 @SuppressWarnings("参数") 参数有很多,可以抑制不同类型的警告,也可以填入 all 全部抑制

    形参是一个 String 数组,所以要注意传入规范

  4. @interface :注解类型 (比如 int 是整形)

四个元注解 (P435)

  1. @Target :修饰注解的注解,称为元注解
  2. @Retention :指定注解的作用范围(SOURCE 源码,CLASS 类,RUNTIME 运行时)
  3. @Documented :指定该注解是否会在javadoc体现
  4. @Inherited :子类会继承父类注解
posted @ 2022-01-10 23:00  Xuuxxi  阅读(59)  评论(0编辑  收藏  举报