# Ⅰ 选择

# 一、知识点

1. 对同一个 package 中的各个类,类的名称不能重复。定义在同一个包(package)内的类可以不经过 import 而直接相互使用。

2.Java 中保留但不允许使用的标识符: goto const

3. 逻辑 / 位运算结合性的优先级:非、与、或

4. 用关键字 static final 修饰属性变量,必须同时定义变量的初始值。

​ <!-- 用关键字 final 修饰属性变量,不必同时定义变量初始值。-->(待确认)

5. byte 8 [-128,127] short 16 int 32 long 64 float 32 double 64 位

6. 计算机是由硬件系统和软件系统两部分组成的。

7. 在程序中书写注释不会影响程序的执行,可以多写一些详细的注释。(√)

8. 基本数据类型是没有静态方法的,但是基本数据类型的包装类却有静态方法

9. 默认的浮点数类型是 double

10.Java 中的接口 (interface) 没有继承 Object 类

11.java 中的包和操作系统没有任何关系,java 的包是用来组织文件的一种虚拟文件系统。

12.main 方法的返回值类型只可以是 void

13. 方法 main 与方法 Main 在 Java 中被认为是不同的方法,可以同时在一个类中定义。

14. 父类的引用指向子类对象,该引用只能调用父类中定义的方法和变量,如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法。

15. 关键字 static 修饰的方法(静态方法),不能直接调用实例方法(不用 static 修饰的方法)。

16. 对于二进制文件的读取,读到文件末尾会抛出 EOFException 异常。

17. 各种数据类型混合运算结果

  • 相同数据类型正常计算

  • short、byte、char 等类型的变量在参与运算时会先被隐含地转换为 int 类型,再参与运算。

  • int 和 long 做运算,则 int 转成 long,最后结果是 long 类型。

  • int 和 float 运算,先有 float 到 double,再由 int 到 double。最后结果是 double。

18. 将 java 文件编译成字节码文件(.class),使用 java 虚拟机(JVM)(在不同操作系统上虚拟机不同)运行字节码文件(将字节码翻译为机器码),计算机硬件直接执行机器码。

19.String 实例一旦创建完毕,就不能再修改其内容。(String 不等同于 char [])

20.this 是对象内部指代自身的引用,同时也是解决成员变量和局部变量同名问题

21. main 函数遵循静态函数的规则,所以每个类都可以写一个 main ,但运行时要选定一个作为程序入口。(一个 Java 文件中可以有多个类,但只能有一个用 public 修饰的类,每个类里都可以有一个 main 方法。因此一个 java 文件可以有多个 main 方法。 )

22. 在一个类的 main 方法中,不能够使用 super 关键字调用父类的方法。

23. static 变量可以不用初始化,默认为 0

24.Java 运行环境(JRE)能够自动完成常规的内存管理,不用显式的释放内存

25.Java 程序的 jar 文件不可以双击执行

# 二、方法

1. 字符串转换为数字

int i=Integer.parseInt("12345");
long l=Long.parseLong("12345");
double d=Double.parseDouble("5.5");
float f=Float.parseFloat("5.5");

2. 最大公约数 最小公倍数

// 最大公约数 (辗转相减法)
public static int gcd(int num1, int num2) {
	while (num1 != num2) {
		if (num1 > num2) {
			num1 = num1 - num2;
		} else {
			num2 = num2 - num1;
		}
    }
    return num1;
}
// 最大公约数(辗转相除法)

// 最小公倍数
public static int lcm(int num1, int num2) {
	return num1 * num2 / gcd(num1, num2);
}

3.break 只能用于 switch 语句和循环语句中

​ continue 只能用于循环语句中

4.return 语句的返回值形式,和方法定义的返回值形式,可以存在默认类型转化关系

  1. 赋值语句

char c=97;
byte a='a';

6. 字符串获取长度的方法: .length() 注意是方法而非属性。

数组 有属性 .length 长度是属性而不是方法,不能写成 .length()

int num[] = {0, 1, 2, 3 };
int a=num.length;
String str="abcdefg";
int b=str.length();

7.main 方法参数必须为字符串数组 (String []),变量名可以随意,通常使用 args

8. 负数对正数取余结果为负数,正数对负数取余结果为正数

-5 % 4 = -1
-5 % -4 = -1
5 % -4 = 1
5 % 4 = 1 

9.switch break default

# 三、类和对象

# (一)变量

1. 局部变量:在方法体中声明的变量

  • 没有默认值,必须初始化

  • 方法结束后,局部变量就会自动销毁

  • 不可以是静态的

  • 访问权限修饰符不能用于局部变量

2. 实例(成员)变量:在类中但在方法体外声明的变量,成员变量定义了类的属性

  • 在对象创建时创建,在对象销毁时销毁
  • byte short int long 默认值为 0, float double 为 0.0, String 为 null
  • 可以被访问权限修饰符修饰
  • 可以被类中方法、构造方法和特定类的语句块访问

3. 静态变量:** 由所有的类实例共享。** 对于类的所有对象,只存在一个静态变量实体。随着类的加载而加载,随着类的销毁而销毁。有默认值,不必初始化。

# (二)静态方法

1. 静态方法:可以通过类名称来调用

2. 非静态方法的调用必须先有一个实例,通过实例来调用

3. 静态方法只能引用静态变量或局部变量

4. 静态方法中不能有 thissuper ,因为 static 属于类的范畴, thissuper 属于对象的范畴。同样静态方法中也不允许出现将成员变量赋值给局部变量的操作

5. 方法的局部变量不允许被 static 修饰

6. 静态方法可以被子类重写,但两个静态方法各自属于各自的类,即当使用多态创建子类对象调用该静态方法时,由于是父类引用,所以会调用父类中的静态方法。若是子类引用指向子类对象调用该静态方法,则调用子类中的静态方法。

7. 被 static 修饰的方法必然也可以重载

# 四、数组

数组是一种对象

# 1. 定义数组:
  • 数组有属性 例: .length

  • 数组创建后长度不可变

  • 定义多维数组,第一维数组的大小必须初始化,其他的不必初始化

    但是使用时必须先对要使用的其他维度的数组进行大小初始化才可以赋值

    int[][][] arr = new int[3][][];

// 定义可以容纳 10 个整数的数组
// 方法一:
int[] scores=new int[10];
int scores[]=new int[10];// 可读性差
// 方法二:
int[] scores;
scores=new int[10];
// 方法三:
int[] scores={99,98,99,97,96,92,93,94,98,99};
// 多维数组
int[][] a=new int[2][3];
int[][] a= {{4, 5, 6},{1, 2, 3}};
// 低维可以不等长
int[][] a = {{1, 2, 3}, {4,5}, {6}};
// 可以逐级创建:
int[][] v = new int[1][]; 
v[0] = new int[2];

int[] arr=new int[] {1,2,3};

# 2.for-each

for(int score:scores)
    System.out.println(score);
// 此 for-each 循环只适用处理从索引 0 开始的所有数组元素
for (int i = 0; i< scores.length; i++)
	System.out.println(scores[i]);

  • For-each 版本的循环,每次把数组中的下一个元素拷贝到循环变量中,对于数组类型是基本数据类型的情况,不会改变原来数组内容

例:

int[] values={1,2,3};
for (int value : values){
	value++;
	System.out.print(value );// 2 3 4
}
for (int value : values){
	System.out.print(value );// 1 2 3 
}

# 3. 可变长度参数表
  • 使用某种特殊语法的形参表,使所定义的方法能接收任意个数的参数,并将参数自动存入数组以便在方法中进行处理。
  • 参数可以是任何类型

public double distance(int ... list)
{
  double sum=0;
    for(int num:list){
        sum=sum+num;
    }
    return sum;
}

  • 可变参数必须写在形参表的最后,且一个方法不能接受两组可变参数

public void test(int count, String name, double ... nums)
{   // whatever		}

  • 可以在构造方法中使用可变参数列表
# 4. 二维数组

public static void main(String[] args)
   {
      int[][] table = new int[5][10];// 行:5 列:10

      // Load the table with values
      //row 行  col 列  
      //table.length:5   table [row].length:row 行的列数
      for (int row=0; row < table.length; row++)
         for (int col=0; col < table[row].length; col++)
            table[row][col] = row * 10 + col;

      // Print the table
      for (int row=0; row < table.length; row++)
      {
         for (int col=0; col < table[row].length; col++)
            System.out.print(table[row][col] + "\t");
         System.out.println();
      }
   }
}

  • 数组的每一维可以有不同的长度,所以高维数组有时也被称为不规则数组

# 五、异常

1. 分类

image-20230123132104985

  • 错误 catch 无意义,无法修复
  • exception
    • checked exception 强制处理异常 (非运行时异常)
      • ClassNotFoundException
      • NoSuchMethodException
      • IllegalAccessException
    • unchecked exception 非强制处理异常 (运行时异常)
      • NullPointerException
      • IndexOutOfBoundsException
      • ArithmeticException
      • StringIndexOutofBoundsException
      • NumberFormatException
      • ClassCastException 强制类型转换

2. 处理程序中的异常,可以使用 try-catch 语句

  • 一个 try 块后跟一个或多个 catch 子句

  • 当 try 块中发生异常时,处理会立即跳转到与异常类型匹配的第一个 catch 子句

  • try 语句可以有一个可选的 finally 子句,该子句始终执行

    • 如果未生成异常,则在 try 块中的语句完成后执行 finally 子句中的语句

    • 如果生成异常,则 finally 子句中的语句将在相应 catch 子句中的语句完成后执行

      结论:finally 中的语句无论如何都会被执行,即使之前有 return 语句,也要先 finally,再 return。 例外是 System.exit ();

3. 可以在方法定义中抛出异常而不是用 catch 语句块进行异常捕获

4.finally 段中的语句,不允许再次出现异常。(×)

5. 通过 throws 关键字在方法上声明该方法要拋出的异常,然后在方法内部通过 throw 拋出异常对象。

6. 自定义异常的对象,只能由 throw 语句手动抛出

7.finally 关键字的代码块中,允许再出现 try…catch… 再次捕获异常。

8. catch 中的语句可以重抛或抛出新异常

9. 所有的异常类是从 Exception 类继承的子类

10.Exception 类是 Throwable 类的子类。除了 Exception 类外,Throwable 还有一个子类 Error 。

11.Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在 Java 程序处理的范畴之外。

12.Error 用来指示运行时环境发生的错误。

例如,JVM 内存溢出。一般地,程序不会从错误中恢复。

13. 异常类有两个主要的子类:IOException 类和 RuntimeException 类。

  1. # public String getMessage() 
    // 返回关于发生的异常的详细信息。
    // 这个消息在 Throwable 类的构造函数中初始化了。 
    <!--code12-->
    自定义异常类的对象可以打印属于自己的异常信息。
  2. # public String toString() 
    // 返回此 Throwable 的简短描述。 
    <!--code13-->

public class StringTest1{
static int getStringLength(){
private String s="Hello Alibaba";// 此处报错!!!不能使用 private 修饰符!!!
int l=s.length();
return l;
 }
public static void main(String[] args){
System.out.println(StringTest1.getStringLength());
 }
}

  • public
    • 类公开可见
    • (成员)数据、方法公开可见
    • 一个 Java 文件中,只能有一个 public 类,遵循封装原则,public 不应该修饰数据成员
  • protected
    • 对于其子类,或者同一个包中的类可见
    • protected 成员,在其他包中只能通过继承的方式来使用
  • 缺省 default
    • 整个类的访问权限是本地
    • 对于同一个包中的类,可见
    • 缺省类,其成员的最高访问权限为缺省,尽管可以用 public,protected 修饰,
  • private
    • 私有,仅对本类或者其外部类公开
  • final
    • 禁止变量被赋值(实现只读效果)
    • 禁止方法被覆写
    • 禁止类被继承
# (三)重写方法 / 方法覆盖

1. 子类中方法名、参数列表与父类中的方法名、参数列表一样时发生重写

2. 返回值类型必须一致(新版本:返回值类型必须小于或者等于父类方法的返回值类型,可以类型转换 )

3. 子类方法访问权限>= 被覆盖的父类方法

4. 子类不能重写父类中用 final 修饰的方法

5. 子类不能重写父类的构造方法

6. 子类无法继承父类的构造函数

方法重载(overloading):

参数个数、类型或顺序不同,方法名相同(返回值类型可以不同)

java 中方法的重写 (overriding) 和重载 (overloading) 是多态性的不同表现

7. 父类被重写后,子类使用 super 调用的是父类的方法(被重写前的),其他情况下,直接调用被重写的方法,调用的就是子类中的方法。

8.java 不支持 super.super 的用法

9. 关键字 final 定义的方法,可以在同一个类中有重载(overloading)方法。(被继承后不能重写)

10. 子类和父类的方法必须都是实例方法才会实现重写,若父类是静态方法,子类是实例方法或者相反都会报错,若父类和子类都是静态方法,那么子类会隐藏父类的静态方法,而不是覆盖。

# (四)影子变量

1. 子类中定义的与父类同名的变量

2. 合法但不提倡

# (五)Object 类

1. 所有的类都直接或间接的由 Object 类派生

2.Java 程序的每一个类都继承 toString 方法和 equals 方法(都是 public)

equals 方法只能比较对象 不能比较基本数据类型变量(int 等)

3.Java 中的接口 (interface) 没有继承 Object 类

# (六)抽象类

public abstract class ABC{}

public abstract void run();

1. abstract 修饰符类为抽象类

2. 抽象类中每一个抽象方法(例: public abstract void run(); )都必须用 abstract 修饰符,但抽象类不必一定包含抽象方法(抽象方法必须在抽象类中,抽象类中可以有抽象方法和其他方法,也可以没有抽象方法)

3. 不能实例化抽象类

4. 抽象类的子类必须重写父类的所有抽象方法,否则它也将被视为抽象类

5. 抽象类中可以有 final 修饰的方法

6.abstract 类不能实例化对象。

7. 抽象方法不能用 final privatestatic 修饰符(这三种修饰符修饰后无法被重写)

  • thisstatic 互斥。
  • abstractstatic 互斥 通过对象引用才能实现多态行为,因此不能是静态方法。
  • abstractprivate 互斥:抽象方法要求子类实现,因此子类必须能够访问该方法。
  • abstractfinal 互斥 抽象方法需要被覆写。
  • private 方法不可能被继承并覆写,所以隐含 final 修饰。
# (七)接口

1. 接口的所有成员都是 public 的,不涉及 protected 和 private 等

2. 子接口继承父接口所有的抽象方法和常量,任何实现子接口的类都必须实现所有的抽象方法

3. 只有定义没有实现 接口中所有方法都没有 body

public interface Doable
{
   public void doThis();
   public int doThat();
   public void doThis2 (double value, char ch);
   public boolean doTheOther (int num);
   default String say(){ return “miaomiao”;}
}

4. 只能继承一个类,却可以继承多个接口

class ManyThings extends ParentClass implements interface1, interface2
{
   // all methods of both interfaces
}

5.Comparable interface 只有一个 compareTo 方法

int result = name1.comareTo(name2);
if (result < 0) //name1 小
   System.out.println(name1 + "comes first");
else
   if (result == 0)
      System.out.println("Same name");
   else //name2 小
      System.out.println(name2 + "comes first");

6. 接口继承另一个接口,用 extends 关键字标明,可以继承多个接口

7. 如果类 B 实现了接口 A(即 B implements A),如果 B 不是抽象类,则必须实现 A 中定义的所有方法。(B 是抽象类,则不必全部实现)

8.interface 中不可以定义属性

9. 接口不能实例化对象,但可以声明对象引用

10.Serializable 接口是一个空接口,没有定义任何方法

# 七、多态

# (一)多态

1. Father obj=new Son(); 先调用父类构造方法再调用子类构造方法

# (二)排序
# 1. 选择排序:

public class sorting{
    // 从大到小
    public static void selectionSort1(int[] list) {
        int max;
        int temp;
        // 这里也可以不减一
        for(int index=0;index<list.length-1;index++){
            max=index;
            for(int scan=index+1;scan<list.length;scan++){
                if(list[scan]>list[max]){
                    max=scan;
                }
            }
            temp=list[index];
            list[index]=list[max];
            list[max]=temp;
        }
    }
   // 从小到大
   public static void selsectionSort2(int[] list){
        int min;
        int temp;
       // 这里也可以不减一
        for(int index=0;index<list.length-1;index++){
            min=index;
            for(int scan=index+1;scan<list.length;scan++){
                if(list[scan]<min){
                    min=scan;
                }
            }
            temp=list[index];
            list[index]=list[min];
            list[min]=temp;
        }
    }
}

# 2. 插入排序

public class sorting{
    // 从大到小
    public static void insertionSort1(int[] list){
        for(int index=1;index<list.length;index++){
            int key=list[index];
            int position=index;
            while(position>0&&key>list[position-1]){
                list[position]=list[position-1];
                position--;
            }
            list[position]=key;
        }
    }

    // 从小到大
    public static void insertionSort2(int[] list){
        for(int index=1;index<list.length;index++){
            int key=list[index];
            int position=index;
            while(position>0&&key<list[position-1]){
                list[position]=list[position-1];
                position--;
            }
            list[position]=key;
        }
    }
}

# 八、递归

# 九、链表

public class ListNode {
    public int data;// 为考试简便,定义属性为 public
    public ListNode link;
    public ListNode(int newData,ListNode newLink){
        data=newData;
        link=newLink;
    }
}

public class List {
    private ListNode head;
}

# (一)基本用语

ListNode p=head;// 指向头节点,准备开始使用 p 操作链表,head 一般不能动
p=p.next;//p 指向下一个节点
p.next=new ListNode(p.next);//p 节点后面插入一个新节点
相当于:
    n=new ListNode();
	n.next=p.next;
	p.next=n;
p.next=p.next.next;// 删除 p 节点的后一个节点
p.next==null;//p 是最后一个节点

# (二)具体方法实现
# 1. 添加头节点

//1. 添加头节点
	public void addHeadNode(int data){
        head=new ListNode(data,head);
	}

# 2. 插入一个节点到链表尾部

//2. 插入一个节点到链表尾部 
	public void addNode(int data){
        // 判断链表是否为空
        if(head==null){
            head=new ListNode(data,head);
        }else{
            ListNode p=head;
            while(p.link!=null){// 寻找尾节点
                p=p.link;
            }
            // 此时 p 为尾节点
            p.link=new ListNode(data,p.link);
        }
    }

# 3. 指定位置插入

//3. 指定位置插入,插入后为 n 号结点,找 n-1 号结点 (链表位置从 0 开始 0 为头节点)
    public void insertNode(int n,int data){
        int length=0;
        ListNode p=head;
        while(p!=null){
            length++;
            p=p.link;
        }
        // 判断插入位置是否合法
        if(n<0||n>length){
            System.out.println("Error!");
            return;
        }
        // 判断是否在头节点插入
        if(n==0){
            head=new ListNode(data,head);
        }
        // 判断是否为链表尾部插入
        else if(n==length){
            p=head;
            while(p.link!=null){
                p=p.link;
            }
            p.link=new ListNode(data,p.link);
        }else {
                p=head;
                for(int i=0;i<n-1;i++){//p 从 0 走 n-1 步,到 n-1 号位置
                    p=p.link;
                }
                p.link=new ListNode(data,p.link);
            }
        }

# 4. 删除指定位置 n 的节点

//4. 删除指定位置 n 的节点 (链表位置从 0 开始 0 为头节点)
    public void deleteNodeByN(int n){
        int length=0;
        ListNode p=head;
        while(p!=null){
            p=p.link;
            length++;
        }
        if(n<0||n>=length){
            System.out.println("Error!");
            return;
        }
        // 判断是否为删除头节点
        if(n==0){
            head=head.link;
        }else{
            p=head;
            for(int i=0;i<n-1;i++){
                p=p.link;
            }
            p.link=p.link.link;
        }
    }

# 5. 删除 data 为 data 的所有节点

//5. 删除 data 为 data 的所有节点
  public void deleteAllNodeByData(int data){
       // 判断链表是否为空
       if(head==null){
           System.out.println("Error!");
           return;
       }
       // 判断是否为头节点删除
       if(head.data==data){
           head=head.link;
       }
       ListNode p=head;
       // 未到达链表尾部就继续找
       while(p.link!=null){
           if(p.link.data==data){
               p.link=p.link.link;
           }
           if(p.link!=null){
               p=p.link;
           }
       }
   }

# 6. 删除 data 为 data 的第一个节点

//6. 删除 data 为 data 的第一个节点
    public void deleteNodeByData(int data){
        // 判断链表是否为空
        if(head==null){
            System.out.println("Error!");
            return;
        }
        // 判断是否为头节点删除
        if(head.data==data){
            head=head.link;
        }
        ListNode p=head;
        // 未到达链表尾部且下一个值为目标值
        while(p.link!=null&&p.link.data!=data){
            p=p.link;
        }
        if(p.link==null){// 说明链表到达尾部仍未找到目标节点
            return;
        }else{
            p.link=p.link.link;
        }
    }

# 7. 对链表节点排序

//7. 对链表节点排序(从小到大)并返回排序后的头节点(采用选择排序)
    public ListNode linkSort(){
        ListNode p=head;
        int temp;
        while(p.link!=null){
            ListNode next=p.link;
            while(next!=null){
                if(next.data<p.data){
                    temp=p.data;
                    p.data=next.data;
                    next.data=temp;
                }
                next=next.link;
            }
            p=p.link;
        }
        return head;
    }

# 8. 获取链表长度

//8. 获取链表长度
    public int length(){
        int length=0;
        ListNode p=head;
        while(p!=null){
            length++;
            p=p.link;
        }
        return length;
    }

# 9. 打印节点

//9. 打印节点
    public void printNode() {
        if(head==null){
            System.out.print("");
        }else{
            ListNode p=head;
            while(p!=null){
                System.out.print(p.data+" ");
                p=p.link;
            }
        }
    }

# 10. 查找正数第 k 个元素

//10. 查找正数第 k 个元素 (从 1 开始,头节点算是第一个)
    public ListNode findNode(int k){
        ListNode p=head;
        // 判断插入位置是否合法
        int length=0;
        while(p!=null){
            length++;
            p=p.link;
        }
        if(k<=0||k>length){
            System.out.println("Error!");
            return null;
        }
        p=head;
        for(int i=0;i<k-1;i++){
            p=p.link;
        }
        return p;
    }

# 11. 查找倒数第 k 个元素

//11. 查找倒数第 k 个元素 ---- 法一:转化为正数第 length-k+1 个  法二:如下
   public ListNode findReverNode(int k){
       ListNode p=head;
       // 判断插入位置是否合法
       int length=0;
       while(p!=null){
           length++;
           p=p.link;
       }
       if(k<=0||k>length){
           System.out.println("Error!");
           return null;
       }
       ListNode first=head;
       ListNode second=head;
       //first 到达正数第 k 处
       for(int i=0;i<k;i++){
           first=first.link;
       }
       //first 从正数第 k 处到链表尾节点相当于 second 从头节点开始到倒数第 k 处
       while(first!=null){
           second=second.link;
           first=first.link;
       }
       return  second;
   }

# 12. 反转链表

//12. 反转链表
    public void reserveLink(){
        ListNode prev=null;// 指向前一个节点
        ListNode curr=head;
        ListNode next;
        while(curr!=null){// 从头节点到尾节点
            next=curr.link;// 先保存下一个节点
            curr.link=prev;// 使当前节点指向前一个结点
            prev=curr;// 前一个结点右移
            curr=next;// 当前节点移向下一个节点
        }
        head=prev;// 更新头节点
    }

# 13. 寻找单链表的中间节点

//13. 寻找单链表的中间节点
    /*
    方法一:先求出链表的长度,再遍历 1/2 链表长度,寻找出链表的中间结点
    方法二:
        用两个指针遍历链表,一个快指针、一个慢指针,
        快指针每次向前移动 2 个结点,慢指针一次向前移动一个结点,
        当快指针移动到链表的末尾,慢指针所在的位置即为中间结点所在的位置
     */
    // 假设链表长度为奇数
    public ListNode findMiddleNode(){
        ListNode slow=head;
        ListNode fast=head;
        while(fast.link!=null){
            fast=fast.link.link;
            slow=slow.link;
        }
        return slow;
    }

# 14. 返回尾节点

//14. 返回尾节点
    public ListNode findLatNode(){
        ListNode p=head;
        while(p.link!=null){
            p=p.link;
        }
        return p;
    }

# 15. 在不知道头结点的情况下删除指定节点

//15. 在不知道头结点的情况下删除指定节点
    /*
    删除结点的重点在于找出其前结点,使其前结点的指针指向其后结点,即跳过待删除结点 n.link=n.link.link;
    1、如果待删除的结点是尾结点,由于单链表不知道其前结点,没有办法删除
    2、如果删除的结点不是尾结点,则将其该结点的值 (n.data) 与下一结点交换 (n.link.data),然后该结点的指针指向下一结点的后续结点 (即删除 n.link)
    (将目标节点转化为目标节点的前节点)
     */
    public void deleteSpecialNode(ListNode n){
        //n 为尾节点
        if(n.link==null){
            System.out.println("Error!");
        }else{
            n.data=n.link.data;
            n.link=n.link.link;
        }
    }

# 16. 两个有序链表排序

//16. 两个有序链表排序(从小到大)
    //n1 n2 为两个链表的头结点
    public ListNode mergeTwoLists(ListNode n1,ListNode n2){
        if(n1==null){
            return n2;
        }else if(n2==null){
            return n1;
        }else{
            // 先确定头结点
            if(n1.data>n2.data){
                head=n2;
                n2=n2.link;
            }else{
                head=n1;
                n1=n1.link;
            }
            ListNode p=head;
            //n1 和 n2 都没有到达尾节点
            while(n1!=null&&n2!=null){
                if(n1.data>n2.data){
                    p.link=n2;
                    n2=n2.link;
                }else{
                    p.link=n1;
                    n1=n1.link;
                }
                p=p.link;
            }
            if(n1==null){
                p.link=n2;
            }else{
                p.link=n1;
            }
            return head;
        }
    }

# 17. 去重

//17. 去重
    public void removeRep(){
        ListNode one=head;//one 作为标杆,寻找并删除和 one 的 data 相同的节点
        ListNode prev;// 指向 two 的前一个节点
        ListNode two;
        //one 从头节点到尾节点
        while(one!=null){
            prev=one;
            two=one.link;
            //two 从此 one 指向节点的下一个节点开始遍历寻找是否有重复节点,有则删除
            while(two!=null){
                if(one.data==two.data){
                    // 或者:
                    //prev.link=two.link;
                    //two=two.link;
                    two=two.link;
                    prev.link=two;
                }else{
                    two=two.link;
                    prev=prev.link;
                }
            }
            one=one.link;
        }
    }

# 18. 取模(取余)

//18. 大数取模(取余) 以对 2022 取模为例
    public int sumMod2022(){
       ListNode p=head;
       int answer=0;
       if(p==null){
           return 0;
       }else if(p.link==null){
           return p.data;
       }else{
           while (p != null) {
               answer=(answer*10+p.data)%2022;
               p=p.link;
           }
       }
       return answer;
   }

# Ⅱ 阅读程序

# 1. 基础

1. 对下列程序,输出结果是:

public class Test{
        private int x=1;
        private static int y=2;
        public static void main(String[] args){
            Test t1=new Test();
            Test t2=new Test();
            t2.x +=2;
            t2.y +=2;
            System.out.print("T1: x="+t1.x+", y="+t1.y+ "; " );
            System.out.println("T2: x="+t2.x+", y="+t2.y);
        }
}

T1: x=1, y=4; T2: x=3, y=4

2. 设 x=1,y=2,z=3, 则表达式 y*=x + z 的值是 8

3. 下列代码的执行结果是( )

public class LogicTest{
      public static void main(String[] args) {
        int a = 2, b = -2, c = 20;
        if ((c --< 30) || (b-- < -2) && (a ++< 3))// 相当于 ((c--<30)||((b--<-2)&&(a++<3)));
            System.out.println("a=" + a + ";b=" + b + ";c=" + c);
        else
            System.out.println(“done”);
  }
}

a=2;b=-2,c=19

  • 逻辑 / 位运算结合性的优先级:非、与、或、
  1. public class Test {
    	public static int a = 1;
    	public static void main(String[] args) {
    		int a = 10;
    		a++; Test.a++;
    		Test t=new Test();
    		System.out.println("a=" + a + " t.a=" + t.a);
    	}
    }
    <!--code43-->
    > 0
    <!--code44-->
    > 1

# 2. 控制流程 + 数组

# 例 1.

public class Two {
    public static void main(String[] args) {
        int a[][]=new int[4][4];
        for(int i=0;i<4;i++)
            for(int j=0;j<4;j++)
                if (i>=j)
                    a[i][j]=i*4+j+1;
                else
                    a[i][j]=0;
        for(int i=0;i<4;i++){
            for(int j=0;j<4;j++)
                System.out.print(a[j][i]+"\t");
            System.out.println();
        }
    }
}

1 5 9 13
0 6 10 14
0 0 11 15
0 0 0 16

# 例 2.

public class TwoFour {
    public static void main(String args[]) {
        int a[][] = new int[5][5];
        int i, j, k = 10;
        for (i = 0; i < 5; i++) {
            for (j = 0; j < 5; j++) {
                if ((i + j) < 5) {
                    a[i][j] = k;
                    k++;
                } else {
                    a[i][j] = 10;
                }
            }
        }
        for (i = 0; i < 5; i++) {
            for (j = 0; j < 5; j++) {
                System.out.print(a[i][j] + " ");
            }
            System.out.println();
        }
    }
}

10 11 12 13 14
15 16 17 18 10
19 20 21 10 10
22 23 10 10 10
24 10 10 10 10

# 3. 异常

# (1)做题方法

①抛出异常,在该方法中寻找 catch

  • 找到:执行该 catch 中语句,执行方法中剩余语句(finally 先与 return 执行)
  • 未找到:执行 finally 语句,再去调用该方法的方法中寻找 catch,以此类推

在方法中抛出异常,只有被 catch 了才能执行剩下的语句,否则无法执行

Exception 是其他异常类的父类,可以捕获所有异常

# (2)例题
# 例 1.

package zero;

public class Three {
    public static void g(int i) throws Exception {
        try {
            if(12/i> 5)
                throw new Exception();
        }
        catch (ArithmeticException e) {
            System.out.println("Divide by Zero!");
        }
        finally {
            System.out.println("Finally 1");
        }
    }
    public static void main(String[] args) {
        int[] array = { 2, 0 };
        for (int i = 0; i<= 2; i++)
            try {
                g(array[i]);
            }
            catch (Exception e) {//Exception 是其他异常类的父类,可以捕获所有异常
                System.out.println("End.");
            }
            finally {
                System.out.println("Finally 2");
            }
    }
}

Finally 1
End.
Finally 2
Divide by Zero!
Finally 1
Finally 2
End.
Finally 2

# 例 2.

public class TwoThree {
    public static void main(String[] args) {
        new ExceptionTest().m1();
    }
}
class ExceptionTest{
    void m1() {
        int[] a = {0, 1 };
        try {
            for (int i=0; i<=a.length; i++) {
                m2(a, i);
                System.out.println();
            }
        }catch (Exception e){
            System.out.println("handle Exception");
        }
        System.out.println(" m1 runs ");
    }
    void m2(int[] a , int i) throws Exception {
        try{
            System.out.println(a[i]/i);
        }catch (ArithmeticException e){
            System.out.println("handle ArithmeticException");
        }
        finally{
            System.out.println("finally");
        }
        System.out.println("m2 ends");
    }
}

handle ArithmeticException
finally
m2 ends

1
finally
m2 ends

finally
handle Exception
m1 runs

# 4. 重载与多态

# (1)创建对象

Father obj1=new Son();// 先调用父类构造方法再调用子类构造方法

Son obj2=new Son();// 先调用父类构造方法再调用子类构造方法

Father obj3=new Father();// 正常调用父类构造方法

# (2)调用方法

obj1.f();

①先去父类中找该方法(一定可以找到,否则编译时会报错,参数类型可以类型转换)

②去子类中看该方法是否被重写(子类中方法名、参数列表、返回值类型与父类中的必须一致才发生重写)

  • 若被重写:调用子类方法
  • 未被重写:调用父类方法

obj2.f();

①去子类找该方法

  • 子类有该方法:使用
  • 子类无该方法:去父类找该方法
    • 父类中有该方法:使用
    • 父类中无该方法:使用子类中相似的(类型转换,选形参类型大于当前类型的)

//父类中调用方法

①父类中寻找该方法(类型转换,选形参类型大于当前类型的)

②去子类中看是否被重写

  • 重写:用重写后方法
  • 未重写:用重写前方法

//子类中调用方法

①在本类中找

  • 有(参数类型相同):使用该子类方法
  • 无:去父类中找
    • 有(参数类型相同):使用父类该方法
    • 无:去子类中寻找类似的方法(参数类型不同,可类型转换)

# 5. 递归

# 例 1.

public class Four{
    public static void perm(int a[], int length, int digit){
        if(digit>length){
            for(int i=0; i<length; i++)
                System.out.print(a[i]);
                System.out.print("\n");
                return;
        }
        for(int i=0; i<length-1; i++)
            if(a[i]==0){
                a[i]=digit;
                perm(a,length,digit+1);
                a[i]=0;
            }
    }
    public static void main(String[] args) {
        int[] a=new int[4];
        for(int i=0; i<a.length; i++)
            a[i]=0;
            perm(a,4,2);
    }
}

2340
2430
3240
4230
3420
4320

# 例 2. 用递归求第 i 个斐波纳契数列数

public class fiboTest {
    public static void main(String[] args){
        int num=fibo(12);
        System.out.println(num);
    }
    
    public static int fibo(int i){
        int one=1,two=1;
        if(i==1||i==2){
            return 1;
        }
        int num=1;
        num=fibo(i-2)+fibo(i-1);

        return num;
    }
}

# 例 3.

public class T24A {
    static int[][] grid = {
            {0,1,0},
            {0,0,0},
            {0,1,0},
    };
    public static void main(String[] args){
        traverse (0, 0);
        System.out.println(grid[0][0] + "\t" + grid[0][2] + "\t" + grid[2][0] + "\t" + grid[2][2] );
    }
    public static boolean traverse (int row, int column) {
        boolean done = false;
        if (valid (row, column)) {
            grid[row][column] = 2;
            if (row == grid.length-1 && column == grid[0].length-1)
                done = true;
            else {
                done = traverse (row+1, column);
                if (!done)
                    done = traverse (row, column+1);
                if (!done)
                    done = traverse (row-1, column);
                if (!done)
                    done = traverse (row, column-1);
            }
            if (done)
                grid[row][column] = 3;
        }
        return done;
    }
    private static boolean valid (int row, int column) {
        boolean result = false;
        if (row >= 0 && row < grid.length &&
                column >= 0 && column < grid[row].length)
            if (grid[row][column] == 0)
                result = true;
        return result;
    }
}

3 0 2 3

# 例 4.

public class T23B {// 汉诺塔
    static String[] s = {null, "S disk","M disk","L disk"};
    public static void main(String[] args) {
        h(3, 'a', 'b', 'c', 3);
    }
    static void m(int diskN, char src, char dst) {
        if (diskN == s.length-1) System.out.println("****");
        System.out.println(s[diskN] + " from " + src + " to " + dst);
    }
    static void h(int n, char s, char t, char d, int diskN) {
        if (n == 1) m(diskN, s, d);
        else {
            h(n - 1, s, d, t, diskN - 1);
            m(diskN, s, d);
            h(n - 1, t, s, d, diskN - 1);
        }
    }
}

S disk from a to c
M disk from a to b
S disk from c to b

****

L disk from a to c
S disk from b to a
M disk from b to c
S disk from a to c

# Ⅲ 写程序

1. 字符串转换为数字

int i=Integer.parseInt("12345");
long l=Long.parseLong("12345");
double d=Doube.parseDouble("5.5");
float f=Float.parseFloat("5.5");

2. 字符串

// 返回字符串中从索引 offset 开始,到 endIndex-1 处的子串
String substring(int offset,int endIndex){}

Edited on Views times