不要像笔者一样,大一不好好学习考的烂,大三又在找补
在Java中,%
是取余运算符:
System.out.println(-7 % 3); // 输出 -1
System.out.println(7 % -3); // 输出 1
这是因为余数的符号与被除数相同。
静态方法中不能有 this
和 super
静态方法属于类,而不是实例,因此不能使用 this
和 super
,它们只能用于实例方法:
public static void staticMethod() {
// this 和 super 在这里不可用
}
权限修饰符只能修饰成员变量和成员方法,不能修饰局部变量
权限修饰符(如 public
, private
)只适用于类的成员,不能用于局部变量:
public void method() {
private int a = 5; // 错误,局部变量不能有访问修饰符
}
接口中可以有具体的成员方法
在Java 8之后,接口可以有默认方法和静态方法,这些方法可以有实现:
interface MyInterface {
default void defaultMethod() {
System.out.println("默认方法");
}
}
Java中只允许父类引用指向子类对象
是Java的多态机制,父类引用可以指向子类对象,但反过来不行:
class Parent {}
class Child extends Parent {}
Parent p = new Child(); // 合法
Child c = new Parent(); // 错误,子类不能指向父类对象
将子类对象强转为父类对象
编译可以通过,但父类引用只能调用父类中的方法:
Child c = new Child();
Parent p = (Parent) c; // 强制转换
将父类对象强转为子类对象时会报错
因为父类对象没有子类的额外成员,强转会失败:
Parent p = new Parent();
Child c = (Child) p; // 错误,父类对象不能转为子类对象
静态方法的重写和多态
静态方法属于类,而不是类的实例,因此静态方法不能像实例方法那样被重写。即使使用多态,静态方法仍然根据引用类型来调用,而不是对象的实际类型。
class Parent {
static void show() {
System.out.println("Parent show()");
}
}
class Child extends Parent {
static void show() {
System.out.println("Child show()");
}
}
public class Test {
public static void main(String[] args) {
Parent p = new Child();
p.show(); // 调用 Parent 类的 show(),静态方法是根据引用类型调用的
}
}
尽管 p
引用的是 Child
类型,但因为是静态方法,实际上调用的是父类 Parent
的 show()
方法。
静态方法可以重载
静态方法是可以重载的,因为方法重载是基于方法签名(即方法名和参数类型)来区分的,而与是否是静态方法无关。
final
修饰符的使用
final
修饰类:表示该类不能被继承。例如,final class MyClass { }
。final
修饰方法:表示该方法不能被重写。final
修饰变量:表示该变量一旦被赋值就不能更改。final
不能用于修饰抽象类和接口,也不能用于修饰抽象方法。
多维数组的初始化
多维数组的第一维必须初始化,而后续维度可以不初始化,但使用时需要初始化。
int[][][] arr = new int[3][][];
arr[0] = new int[2][];
arr[0][0] = new int[5];
String str = "CHASD5SH";
int doub = str.charAt(5);
System.out.println(doub);
str.charAt(5)
获取的是字符串 str
中索引为 5
的字符。
str = "CHASD5SH"
,所以 str.charAt(5)
返回的是字符 '5'
。在 Java 中,字符是基于 Unicode 编码的。字符 '5'
的 Unicode 编码是 53。
char
类型实际上是一个整数,表示该字符的 Unicode 编码。因此,charAt(5)
返回的是字符 '5'
,将其赋值给 int doub
,结果是字符 '5'
的整数值 53。
String
类的 compareTo()
方法返回的值是基于字符的 Unicode 编码值的差异。返回值的具体含义取决于字符串的字典顺序。
compareTo()
返回 0
。自动包装(自动装箱与拆箱)
自动包装是指Java语言中的基本数据类型与对应的包装类之间的自动转换。例如,int
类型自动转换为 Integer
类,double
自动转换为 Double
类,等等。
自动装箱(Autoboxing):将基本数据类型转换为对应的包装类对象。
例如:
int a = 5;
Integer b = a; // 自动装箱:int 转为 Integer
自动拆箱(Unboxing):将包装类对象转换为对应的基本数据类型。
例如:
Integer b = 10;
int a = b; // 自动拆箱:Integer 转为 int
Java 会自动完成这些转换,因此我们不需要显式地调用构造函数来进行转换。这个机制使得基本数据类型和包装类之间的转换更加简便。
包的基本概念:
包是类的组织方式,用来组织类文件,以避免类名冲突,并可以实现访问控制。
import
)。如果不同包之间的类需要相互访问,就需要使用 import
来导入。public
、private
、protected
)控制访问权限。阅读下面异常处理程序,写出执行结果(6分)
public class ExceptionDemo {
public static void main(String[] args) {
ExceptionTest et=new ExceptionTest(); et.m1(); }
}
class ExceptionTest{
int i;
int[] a = new int[5];
void m1() {
try { while (true) {
m2();
System.out.println(); }
}catch (Exception e) {
System.out.println(" m1 runs "); }
}
void m2() throws Exception {
try{ System.out.println(10/i);
System.out.println(a[i]);
}catch (ArithmeticException e) {
i = 10;
System.out.println("handle ArithmeticException"); }
Finally {
System.out.println("finally"); }
System.out.println("m2 ends");
}
}
m1
方法包含一个无限循环 while (true)
,每次循环都会调用 m2
方法。m2
抛出异常,m1
会捕获到该异常并输出 " m1 runs "
。m2
方法首先尝试执行 10 / i
,然后尝试打印数组 a[i]
的值。ArithmeticException
(例如,i
为 0 时),就会进入 catch
块,打印 "handle ArithmeticException"
,并将 i
设置为 10。finally
块都会执行,打印 "finally"
。finally
块之后,m2
会输出 "m2 ends"
。第一次调用 m2()
:
i
默认值是 0
(因为它是类的实例变量)。10 / i
会导致 ArithmeticException
,因为除以 0 会抛出异常。catch
块,打印 "handle ArithmeticException"
,并将 i
设置为 10
。finally
块执行,打印 "finally"
。m2 ends
被打印。第二次调用 m2()
:
i
已经被修改为 10
,执行 10 / i
会输出 1
。a[i]
,即 a[10]
,但是数组 a
只有 5 个元素,因此会发生 ArrayIndexOutOfBoundsException
异常。m2
中没有捕获 ArrayIndexOutOfBoundsException
,这个异常会传递给 m1
,m1
会捕获到这个异常并输出 " m1 runs "
。m1
捕获异常:
m2
抛出异常时,m1
捕获到异常并输出 " m1 runs "
,然后退出。执行结果:
handle ArithmeticException
finally
m2 ends
m1 runs
对于类的实例变量(如 int i 和 int[] a),它们会自动初始化为默认值:0 和 null。
Java 编译过程从源代码(.java
)开始,通过编译器生成字节码(.class
文件),然后通过 JVM 来运行。
错误分为语法错误、运行时错误和逻辑错误,每种错误都有不同的表现和调试方式。
要运行 Java 程序,系统需要安装 JDK(用于开发)或者 JRE(仅用于运行)。
在 Java 中,变量可以分为 原型类型(基本类型)和 类类型(引用类型):
原型类型(基本类型):
这些类型是 Java 中最基础的类型,它们直接存储值,而不涉及对象的引用。原型类型有 8 种:
true
或 false
类类型(引用类型):
类类型是通过引用来指向对象的,它们可以是任何类类型(如 String
、Object
)以及数组。类类型的变量保存的是对象在内存中的地址,而不是对象本身。
标识符的命名规则
标识符是 Java 中用来命名变量、方法、类等的名称,命名时需要遵守一定的规则:
_
)或美元符号($
)开头。
_variable
,$value
,myVar
_
)和美元符号($
)。
my_var123
,value$
,num_1
int
、class
、if
、else
等),这些关键字不能作为标识符。$
:
$
),例如:$value
,$testVar
。但是它通常用于自动生成的代码,不建议在自定义标识符中使用 $
。my-variable
是非法的,my_variable
才是合法的。myVar
和 myvar
是两个不同的标识符。Java 区分大小写:
Return
不是关键字,但是因为它首字母大写,可以作为变量名,虽然这不符合 Java 命名习惯。return
是关键字,表示方法的返回语句,不能作为标识符使用。字符串常用方法:
length()
:返回字符串的长度(字符数)。
String str = "Hello";
System.out.println(str.length()); // 输出 5
toLowerCase()
:将字符串转换为小写字母。
String str = "HELLO";
System.out.println(str.toLowerCase()); // 输出 "hello"
substring()
:提取字符串的子字符串。可以传入起始和结束索引。
String str = "Hello";
System.out.println(str.substring(1, 4)); // 输出 "ell" (从索引1到3的字符)
replace()
:替换字符串中的字符或子字符串。
String str = "Hello World";
System.out.println(str.replace("World", "Java")); // 输出 "Hello Java"
charAt()
:返回指定索引位置的字符。
String str = "Hello";
System.out.println(str.charAt(1)); // 输出 "e" (索引从 0 开始)
toCharArray()
:将字符串转换为字符数组。
String str = "Hello";
char[] chars = str.toCharArray();
System.out.println(chars[0]); // 输出 'H'
比较两个字符串的长度
在 Java 中,比较字符串长度时,需要使用 length()
方法。length()
方法返回字符串的长度(即字符的数量),而不是 equals()
方法,后者是用来比较两个字符串的内容是否相等的。
String
在 Java 中是 不可变的(immutable),这意味着一旦创建了一个 String
对象,它的内容不能被更改。如果你尝试修改字符串,实际上是创建了一个新的字符串对象。
import
语句用于引入外部类库或包中的类,以便在程序中使用它们。可以引入整个包,或者只引入某个具体的类。
import java.util.Scanner; // 引入 Scanner 类
import java.util.*; // 引入整个 java.util 包
int
和 Integer
的转换:
int
转为 Integer
:使用 Integer.valueOf()
或自动装箱。Integer
转为 int
:使用 intValue()
或自动拆箱。Math
类提供了许多常用的数学方法,包括求绝对值、平方根、最大值、最小值等。
常用方法:
Math.abs()
:返回绝对值。
System.out.println(Math.abs(-10)); // 输出 10
Math.sqrt()
:返回平方根。
System.out.println(Math.sqrt(16)); // 输出 4.0
Math.max()
和 Math.min()
:返回两个数中的最大值或最小值。
System.out.println(Math.max(5, 10)); // 输出 10
Math.pow()
:返回一个数的指数幂。
System.out.println(Math.pow(2, 3)); // 输出 8.0
静态变量(Static Variables)
class MyClass {
static int count = 0; // 静态变量,初始值为 0
}
public class Main {
public static void main(String[] args) {
System.out.println(MyClass.count); // 通过类名访问静态变量
MyClass obj1 = new MyClass();
MyClass obj2 = new MyClass();
System.out.println(obj1.count); // 通过对象引用访问静态变量
obj1.count = 5; // 修改静态变量的值
System.out.println(obj2.count); // 由于静态变量是类共享的,obj2.count 会输出 5
}
}
方法中的参数:传值与传址
在 Java 中,方法的参数传递有两种方式:传值(Pass-by-Value) 和 传址(Pass-by-Reference)。
原型类型(基本类型)参数:传值
int
, char
, double
等)传递的是值的副本,即在方法调用时,实参的值会复制给形参。方法内部对形参的修改不会影响到实参。示例:
class Test {
public static void changeValue(int x) {
x = 100; // 仅修改了 x 的副本
}
public static void main(String[] args) {
int num = 10;
changeValue(num);
System.out.println(num); // 输出 10,原始变量没有改变
}
}
changeValue
方法中,x
是 num
的副本。对 x
的修改不会影响原始的 num
。对象参数:传址
示例:
class Person {
String name;
Person(String name) {
this.name = name;
}
}
class Test {
public static void changeName(Person p) {
p.name = "John"; // 修改了 p 对象的属性
}
public static void main(String[] args) {
Person person = new Person("Alice");
changeName(person);
System.out.println(person.name); // 输出 John,原始对象的属性被修改
}
}
p
是 person
对象的引用副本。通过 p
修改了对象的属性,影响到了原始的 person
对象。矩阵乘法(嵌套循环)
for (int i = 0; i < A行; i++)
for (int j = 0; j < B列; j++)
for (int k = 0; k < A列; k++)
C[i][j] += A[i][k] * B[k][j];
满足条件的数筛选(如个位+十位和等于某值)
for (int i = 1000; i <= 1800; i++) {
int ge = i % 10;
int shi = (i / 10) % 10;
if (ge + shi == 9) System.out.println(i);
}
重载(Overloading)
同一类中允许多个方法同名但参数不同;
参数不同指的是:数量不同、类型不同、顺序不同;
❌ 不能仅通过返回值不同来区分重载!
构造函数的访问修饰符不一定必须是 public
,但一般这么写是为了让类可以被外部实例化。
选择排序:
public void selectionSort(int[] a) {
for (int i = 0; i < a.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < a.length; j++) {
if (a[j] < a[minIndex]) {
minIndex = j;
}
}
int temp = a[i];
a[i] = a[minIndex];
a[minIndex] = temp;
}
}
二分查找(数组需先排序):
public int binarySearch(int[] number, int searchValue) {
int low = 0, high = number.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (number[mid] == searchValue)
return mid;
else if (number[mid] < searchValue)
low = mid + 1;
else
high = mid - 1;
}
return -1;
}
可以写递归形式的二分查找:
public int recursiveBinarySearch(int[] arr, int low, int high, int key) {
if (low > high) return -1;
int mid = (low + high) / 2;
if (arr[mid] == key) return mid;
else if (arr[mid] < key) return recursiveBinarySearch(arr, mid + 1, high, key);
else return recursiveBinarySearch(arr, low, mid - 1, key);
}
static + final 的变量是否必须赋初值?
是的,必须。
final static int MAX = 100; // ✅ 必须立刻赋初值
因为 static final
相当于一个 常量(compile-time constant),一旦定义必须马上确定其值。
抽象类与抽象方法(abstract)
抽象方法:
abstract void draw();
)static
方法属于类本身,而不是类的实例。抽象方法需要被子类实例实现,因此不能是静态的。)抽象类:
可以没有抽象方法
如果至少包含一个抽象方法,必须被声明为 abstract
类
可以包含非抽象方法,即普通方法、构造方法、属性等
子类中必须实现所有抽象方法,否则该子类也必须声明为 abstract
abstract 可以修饰:
在动态绑定和多态中,Java 运行时会根据对象的实际类型来决定调用哪个方法
示例代码:
class Z {
void f(int x) { System.out.println("Z int"); }
void f(double x) { System.out.println("Z double"); }
}
class P extends Z {
void f(int x) { System.out.println("P int"); }
// void f(double x) { System.out.println("P double"); } // 可能有也可能没有
}
public class TestOverride {
public static void main(String[] args) {
P t = new P();
t.f(10); // P int
t.f(10L); // 10L是long,会优先匹配 f(long) → 没有 → 看 f(double)
t.f(2.5); // P 或 Z 的 f(double)
}
}
P t = new P()
)t.f(10)
10
是 int
类型,所以会调用 P
中的 f(int x)
方法。输出:
P int
t.f(10L)
10L
是 long
类型,会先检查 P
类中是否有 f(long x)
,但是没有找到该方法,所以会去查找 Z
类中的 f(double x)
。由于 long
可以隐式转换为 double
,会调用 Z
中的 f(double x)
。输出:
Z double
t.f(2.5)
2.5
是 double
类型,会首先调用 P
类中的 f(double x)
,如果 P
中没有这个方法,则会调用 Z
中的 f(double x)
。在当前代码中,P
类没有 f(double x)
,所以会调用 Z
中的 f(double x)
。输出:
Z double
P
中的 f(double x)
方法后如果删除 P
中的 f(double x)
方法,则调用 t.f(10L)
和 t.f(2.5)
时都会调用 Z
中的 f(double x)
,因为 P
类没有匹配的方法,且 long
和 double
可以互相隐式转换。
t.f(10L)
long
类型会隐式转换为 double
,所以调用 Z
中的 f(double x)
方法。输出:
Z double
t.f(2.5)
直接调用 Z
中的 f(double x)
方法。输出:
Z double
main
方法为 Z t = new P();
在这种情况下,变量 t
是 Z
类型的引用,指向一个 P
类型的对象。此时,方法调用仍然是根据实际对象的类型来决定的(即 P
类型的对象),但是 Z
类中的方法是最先被查找的。
t.f(10L)
由于 t
的类型是 Z
,它会首先查找 Z
类中的方法。如果 Z
中没有 f(long x)
,就会向上查找 f(double x)
。由于 Z
中有 f(double x)
,它会调用 Z
中的 f(double x)
。输出:
Z double
t.f(2.5)
同样地,t
会首先查找 Z
中的 f(double x)
,然后调用它。输出:
Z double
Z
中没有 f(double x)
方法如果 Z
中删除了 f(double x)
方法,且 P
中也没有 f(double x)
方法,那么编译时会报错,因为没有找到匹配的方法。
t.f(10L)
和 t.f(2.5)
如果 Z
和 P
都没有 f(double x)
,则会发生编译错误,因为 Java 无法找到一个合适的方法来匹配 long
或 double
类型的参数。
编译错误:方法 f(double) 在类 Z 中不存在
Z
中添加了 f(long x)
方法如果在 Z
中添加了 f(long x)
方法,则当调用 t.f(10L)
时会调用 Z
中的 f(long x)
方法。
t.f(10L)
会执行 Z.f(long x)
,因为 long
类型会直接匹配 Z
中的 f(long x)
。输出:
Z long
t.f(2.5)
仍然会调用 Z.f(double x)
,因为 double
类型直接匹配 Z
中的 f(double x)
方法。输出:
Z double
Java支持单继承 + 多接口实现
class Parent {}
interface A {}
interface B {}
class Child extends Parent implements A, B {}
即使 try
和 catch
中 return
了,也会先执行 finally 再返回值
Java 中的异常处理有两种主要方式:
一种是捕获并处理异常(try-catch-finally):
这是最常见的方式,如果你知道如何处理某种异常,就在方法中使用 try-catch
:
try {
int a = 10 / 0; // 可能抛出 ArithmeticException
} catch (ArithmeticException e) {
System.out.println("发生算术异常:" + e.getMessage());
} finally {
System.out.println("无论是否发生异常,我都会执行");
}
另一种是声明异常并抛出(throws / throw):
如果你不打算在当前方法中处理异常,就使用 throws
把异常交给调用者去处理(声明),或用 throw
主动抛出异常对象:
声明异常:
public void readFile(String path) throws IOException {
FileReader reader = new FileReader(path); // 可能抛出IOException
}
主动抛出异常:
public void checkAge(int age) {
if (age < 18) {
throw new IllegalArgumentException("未满18岁不允许注册");
}
}
❗注意:
try/catch
处理部分异常,剩下的用 throws
向外声明。链表基本结构回顾
ListNode 类(结点类)
public class ListNode {
private String data;
private ListNode link;
public ListNode(String data, ListNode link) {
this.data = data;
this.link = link;
}
public String getData() { return data; }
public ListNode getLink() { return link; }
public void setData(String data) { this.data = data; }
public void setLink(ListNode link) { this.link = link; }
}
StringLinkedList 类中的常用方法实现
length()
public int length() {
int count = 0;
ListNode position = head;
while (position != null) {
count++;
position = position.getLink();
}
return count;
}
addANodeToStart(String addData)
public void addANodeToStart(String addData) {
head = new ListNode(addData, head);
}
deleteHeadNode()
public void deleteHeadNode() {
if (head != null)
head = head.getLink();
}
find(String target)
public boolean find(String target) {
ListNode position = head;
while (position != null) {
if (position.getData().equals(target))
return true;
position = position.getLink();
}
return false;
}
showList()
public void showList() {
ListNode position = head;
while (position != null) {
System.out.println(position.getData());
position = position.getLink();
}
}
将链表所有数据转入 ArrayList
:
ArrayList<String> list = new ArrayList<>();
ListNode position = head;
while (position != null) {
list.add(position.getData());
position = position.getLink();
}
插入新节点:
public void insertAfter(String target, String newData) {
ListNode position = head;
while (position != null) {
if (position.getData().equals(target)) {
ListNode newNode = new ListNode(newData, position.getLink());
position.setLink(newNode);
return;
}
position = position.getLink();
}
}
删除目标节点后面的节点:
public void deleteAfter(String target) {
ListNode position = head;
while (position != null) {
if (position.getData().equals(target) && position.getLink() != null) {
position.setLink(position.getLink().getLink());
return;
}
position = position.getLink();
}
}
基类方法为public int a(int x, int y); 派生类方法为public void a(int x, int y); 可以这样实现 覆盖方法吗?
答: 不可以. 编译将报错,因为这既不是方法的重载(译文版教材: 无法根据返回值的类型来实现重载), 也不是方法的覆盖(方法名之前的修饰符要完全一致, 方法名之后的参数数目, 类型要完全一样 )
若在派生类中自己编写缺省构造函数, 而在里面又只初始化了部分的实例变量, 那么另外的实例变量的值是否还被自动初始化?
答: 现在版本的java仍将对缺省构造函数中未能涉及到的实例变量进行初始化
构造函数与一般的方法有何区别?
表达式“i=4; i+++i+++i++; ”的值是多少 ?
答:结果为15; 上式可以理解为(i++)+(i++)+(i++), 即: 4+5+6=15; 因为++的优先级比+高,java编译器将首先组织优先级较高的运算符组成表达式.
Java源文件命名时大小写不敏感
一个Java程序可以编译成多个class文件
抽象类不一定**非得有抽象方法,例如为了防止创建对象,也可定义成 abstract。
Java 不允许在类型声明中 指定数组的大小。也就是说:
int[3] a = {1, 2, 3}; // ❌ 错误:不能写 int[3]
正确的 Java 数组声明方式是:
方式 1:自动推断长度
int[] a = {1, 2, 3}; // 正确,长度自动为 3
方式 2:使用 new 显式声明并初始化
int[] a = new int[]{1, 2, 3}; // 正确
方式 3:只声明长度,稍后赋值
int[] a = new int[3]; // 创建长度为 3 的数组,初始值全为 0
a[0] = 1;
a[1] = 2;
a[2] = 3;
重写(override)时,返回类型必须相同(或协变返回类型)重载是通过参数列表的不同(个数、顺序或类型)来区分的,返回值类型不同不能作为重载的唯一依据。
输出结果是什么?
public class Test {
int a[] = new int[10];
public static void main ( String arg[] ) {
System.out.println ( a[6] );
}
}
正确答案:C. 会出现编译错误
解析:
变量 a
是一个非静态成员变量,但 main
方法是 static
的。
在静态方法中不能直接访问非静态成员,所以 a[6]
会导致编译错误。
正确写法应该是:
public static void main(String[] args) {
Test t = new Test();
System.out.println(t.a[6]); // 输出 0
}
this
表示当前对象的引用。this
只能在 非 static 方法或构造方法中使用,不能在 static 方法中使用。this
是和具体对象绑定的,不是和类绑定。
关于 throws 和 throw 的说法错误的是?
选项:
原因分析:
MyException extends Exception
,然后用 throw new MyException();
抛出。throw
抛出的是受检异常(checked exception),必须处理(try-catch)或声明(throws)。否则编译报错。这条语句是不准确的。throw
是用来“抛出”异常的,它不能“声明”你不想处理,而是你“现在就要抛”!throws
,而不是 throw
。深入解析 Java 中的 throw 和 throws:异常处理机制的核心_java throw-CSDN博客
public class T22A {
public static void main(String\[] args) {
String\[] codes = { "2021", "202b","202" };
try {
for (int i = 0; i <= codes.length; i++) {
try {
System.out.println("Analyzing: " + codes\[i]);
analyzeCode(codes\[i]);
} catch (NumberFormatException e) {
System.out.println("Not a Number: " + codes\[i]);
} finally{
System.out.println("\*\*\*\*\*\*\*\*");
}
}
} catch (Exception e) {
System.out.println("Unknown Exception ");
}
System.out.println("Analyze Completed ");
}
public static void analyzeCode(String code) throws Exception {
if (code.length() != 4) throw new Exception();
System.out.println("successful : " + Integer.parseInt(code));
}
}
运行结果:
Analyzing: 2021
successful : 2021
********
Analyzing: 202b
Not a Number: 202b
********
Analyzing: 202
Unknown Exception
Analyze Completed
codes[0] = "2021"
。analyzeCode
检查长度合法(4),并成功解析为整数,输出 successful : 2021
。finally
块执行,输出 *********
。codes[1] = "202b"
。analyzeCode
检查长度合法(4),但解析时遇到非数字字符 b
,抛出 NumberFormatException
。catch
捕获异常,输出 Not a Number: 202b
。finally
块执行,输出 *********
。codes[2] = "202"
。analyzeCode
检查长度不合法(3),直接抛出 Exception
。try
块没有匹配的 catch
,异常传递到外层。finally
块,输出 *********
。catch
捕获通用 Exception
,输出 Unknown Exception
。i=3
,避免数组越界)。Analyze Completed
。第三次循环(i=2)发生了什么?
codes[2] = "202"
,调用 analyzeCode("202")
。analyzeCode
检查 code.length() != 4
,发现长度为 3,直接抛出 Exception
。try-catch
没有捕获 Exception
(它只捕获 NumberFormatException
),所以异常会向外层传播。try-catch
(main
方法的 try
块),被 catch (Exception e)
捕获,输出 Unknown Exception
。try
块会立即终止,包括其中的 for
循环。(因为try只按顺序执行一次,外面的try执行完了,里面的for循环自然就不执行了)在一个 Java 源文件中,只能有一个 public class
。如果尝试在一个源文件中定义多个 public class
,编译器会报错。因为 Java 的设计规范规定,每个源文件最多只能有一个公共类,并且源文件的名称必须与该公共类的名称完全相同。不过,可以在一个源文件中定义多个非 public class
,这些类可以用 private
、protected
或默认的访问修饰符。
stu&sdu
&
这样的符号。&
用于位运算或表示参数化类型的交界类型等,不是用于组成标识符的有效字符。在 Java 中,字符串的长度是通过 length()
方法来获取的,而数组的长度是通过直接访问 length
属性(而不是方法)来获取的。例如,对于字符串 String str = "hello";
,可以用 str.length()
获取长度;对于数组 int[] arr = new int[5];
,则通过 arr.length
获取长度
在 Java 中,二维数组的声明格式为 int a[][]
或 int[] a[]
或 int[][] a
。正确的初始化方式是 int a[][] = { {1,2}, {3,4}, {5,6} };
或者 int a[][] = new int[3][2];
然后给数组元素赋值。选项 A 中的 [3][2]
放在数组名后面是不正确的,应该将 new
关键字放在数组声明前面,例如 int a[][] = new int[3][2];
或者直接初始化为具体的值。
static
不能用于修饰局部变量(即在方法内部定义的变量)。
public class T23B {
public static void main(String[] args){
new B().m(2022);
new A(2021).m(2020);
}
}
class B{
public B(){
System.out.println("Init B: ");
}
public void m(int x){ f(g(x)); }
public void f(int x){
System.out.println("\tf() in B");
g(x);
}
private int g(int x){
System.out.println("\tg(int) in B");return x;
}
}
class A extends B {
public A(int x){
System.out.println("Init A: " + x);
}
public void f(int x){
System.out.println("\tf() in A");
g(x);
}
public int g(int d){
System.out.println("\tg(int) in A");
return (int)d;
}
}
运行结果
Init B:
g(int) in B
f() in B
g(int) in B
Init B:
Init A: 2021
g(int) in B//这个很奇怪
f() in A
g(int) in A
如果一个数等于它的因子之和,则称该数为“完数”(或“完全数”)。例如,28的因子为1、2、4、7、14,而 28=1+2+4+7+14,因此28是“完数”。写一个程序输出10000以内的所有完数。(8分)
public class PerfectNumber {
public static void main(String[] args) {
// 输出10000以内的完数
for (int i = 1; i <= 10000; i++) {
if (isPerfect(i)) {
System.out.println(i);
}
}
}
// 判断一个数是否为完数
public static boolean isPerfect(int number) {
int sumOfDivisors = 0;
// 计算该数的所有真因子之和
for (int i = 1; i <= number / 2; i++) {
if (number % i == 0) {
sumOfDivisors += i;
}
}
// 如果真因子之和等于该数,则返回true
return sumOfDivisors == number;
}
}
输入两个小于1~1000位的超大整数,输出它们的和(注意:因为位数很大,会超过java的整数范围,直接用整数输入是不合适的)。(10分)样例: 输入: 917654321454365454445435 87654321987654321987654321 输出:87746087419799758532099756(以上两数相加)
import java.math.BigInteger;
import java.util.Scanner;
public class LargeNumberAddition {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取两个大整数
System.out.println("请输入第一个超大整数:");
BigInteger num1 = new BigInteger(scanner.nextLine()); // 读取第一个数字
System.out.println("请输入第二个超大整数:");
BigInteger num2 = new BigInteger(scanner.nextLine()); // 读取第二个数字
// 计算它们的和
BigInteger sum = num1.add(num2);
// 输出结果
System.out.println("它们的和是:" + sum);
}
}
返回链表中最大值出现的次数
public int maxCount() {
if (head == null) return 0; // 如果链表为空,直接返回 0
int count = 1; // 初始化计数器,最大值出现次数
ListNode maxNode = head; // 设置最大节点为头节点
int maxValue = maxNode.data; // 获取头节点的数据作为初始最大值
maxNode = head.link; // 移动到下一个节点
// 遍历链表
while (maxNode != null) {
if (maxNode.data > maxValue) { // 如果发现更大的值
maxValue = maxNode.data; // 更新最大值
count = 1; // 重置计数器为 1,因为找到了新的最大值
} else if (maxNode.data == maxValue) { // 如果找到了相等的最大值
count++; // 计数器加 1
}
maxNode = maxNode.link; // 继续遍历下一个节点
}
return count; // 返回最大值出现的次数
}
返回链表倒数第 k
个节点
public ListNode findKthToTail(int k) {
// 快慢指针法
ListNode fast = head;
ListNode slow = head;
// 移动 fast 指针,前进 k 步
for (int i = 0; i < k; i++) {
if (fast == null) {
return null; // 如果 k 大于链表长度,返回 null
}
fast = fast.link;
}
// 快指针和慢指针同时移动,直到快指针到达末尾
while (fast != null) {
fast = fast.link;
slow = slow.link;
}
return slow; // slow 即为倒数第 k 个节点
}
方法的返回值可以是基本数据类型,也可以是对象类型,包括自定义类的实例。
int
是基本数据类型,不能直接调用 equals()
方法
continue
语句用于终止当前循环的迭代,跳过本次循环中剩余的代码,然后继续下一次循环迭代。
并非程序中所有可能出现的异常都必须在 catch
块中捕获。非检查异常(如 RuntimeException
及其子类)不需要显式捕获,而检查异常需要要么被捕获,要么用 throws
声明。
在方法定义中使用 throws
关键字声明可能出现的异常,即使在方法调用时异常没有实际抛出,也不会影响代码的编译。throws
声明用于告知调用者该方法可能会抛出的异常类型。
class Animal{
public String name;
public Animal(String s){
name = s;
}
public void move(){
System.out.println(this + "在运动");
}
public String toString(){
return "动物" + name;
}
}
class Bird extends Animal{
public Bird(String s){
super(s);
}
public void move(){
System.out.println(this + "在飞行");
}
public boolean equals(Object other){
System.out.print(name + " Object 判断相等 ");
return super.equals(other);
}
public boolean equals(Bird other){
System.out.print(name + " Bird 判断相等 ");
return true;
}
public String toString(){
return "小鸟" + name;
}
}
public class T24A {
public static void main(String\[] args){
Bird\[] blist = {new Bird("麻雀"),new Bird("鸿鹄")};
Animal\[] alist = {new Animal("大黄"), blist\[0],blist\[1]};
for (Animal a : alist) a.move();
System.out.println(alist\[0].equals(alist\[2]));;
System.out.println(alist\[1].equals(blist\[0]));
System.out.println(blist\[1].equals(blist\[0]));
}
}
答案:
动物大黄在运动
小鸟麻雀在飞行
小鸟鸿鹄在飞行
false
麻雀 Object 判断相等 true
鸿鹄 Bird 判断相等 true
编译时,根据引用类型,去对应的类中找参数列表符合的方法,锁定该方法;运行时,根据实际类型,去对应的子类中找刚刚锁定的方法的重写方法,没有就用刚刚的。
n个猴子围成一圈选大王,依次1-7循环报数,报到7的猴子被淘汰,直到最后一只猴子称为大王,写一个方法public static int monkeyKing(int n),返回第几只猴子会成为大王?(10分)
public static int monkeyKing(int n) {
boolean[] b=new boolean[n];
for(int i=0;i<b.length;i++){
b[i]=true;
}
int num=0;
int monkeyLeft=n;
int index=0;
while(monkeyLeft>1){
if(b[index]){
num++;
if(num==7){
b[index]=false;
monkeyLeft--;
num=0;
}
}
index++;
if(index==n){
index=0;
}
}
//遍历数组,找到最后活着的那个猴子王
for(int i=0;i<b.length;i++){
if(b[i]){
return i+1;
}
}
return 0;
}
public void removeRep ( ){
// 删除链表中值重复出现的节点
// 如假设当前链表为1->2->4->4->3->3->2->1->1,
// 删除值重复的节点之后为1->2->4->3
}
public void removeRep ( ){
ListNode cur=head;
ListNode pre=null,next=null;
while(cur!=null){
pre=cur;
next=cur.link;
while(next!=null){
if (cur.data==next.data)
pre.link=next.link;
else
pre=next;
next=next.link;
}
cur=cur.link;
}
}
public void removeRep() {
Set<Integer> seen = new HashSet<>(); // 用于记录已经遇到过的值
ListNode current = head;
ListNode previous = null;
while (current != null) {
if (seen.contains(current.data)) { // 如果值已出现过,删除节点
previous.link = current.link; // 跳过当前节点
} else {
seen.add(current.data); // 记录当前节点的值
previous = current; // 继续前进
}
current = current.link; // 移动到下一个节点
}
}
public void reversePart ( int from , int to ) {
//在链表中把第from个节点到第to个节点这一部分进行反转,假设 02->3->4->5->null,from=2,to=4
// 调整结果为1->4->3->2->5
//注意不使用额外的链表、堆栈等结构,不能填写到数组中,操作后再创建链表
public void reversePart(int from, int to) {
if (from >= to) return; // 输入参数非法(from < to)
ListNode dummy = new ListNode(); // 创建一个虚拟头节点,简化边界情况的处理
dummy.link = head;
ListNode preFrom = dummy; // preFrom 指向 from 前一个节点
// 找到 from 的前一个节点
for (int i = 0; i < from - 1; i++) {
preFrom = preFrom.link;
}
ListNode fromNode = preFrom.link; // 从第 from 个节点开始
ListNode toNode = fromNode;
for (int i = from; i < to; i++) {
toNode = toNode.link;
}
// 保存 toNode 后面的节点
ListNode nextToNode = toNode.link;
// 将从 from 到 to 的部分反转
ListNode prev = null;
ListNode current = fromNode;
while (current != nextToNode) {
ListNode next = current.link;
current.link = prev;
prev = current;
current = next;
}
// 将反转后的链表重新连接
preFrom.link = prev;
fromNode.link = nextToNode;
if (from == 1) {
head = prev; // 如果反转的是从头开始的部分,更新头节点
}
}
package
语句定义的包名必须与文件的目录结构一致。例如,如果代码中使用 package com.example;
,那么该文件必须位于 com/example
目录下。
this
关键字只能在实例方法中使用,因为它指向当前对象。而 main
方法是静态的,静态方法是与类相关的,而不是与某个特定的对象相关,因此不能在 main
方法中使用 this
。
abstract
方法不能被声明为 static
,因为 abstract
方法需要被子类实现,而 static
方法属于类本身,不能被子类重写。
static
方法不能直接访问非 static
属性。因为 static
方法属于类而不是对象,非 static
属性属于对象实例,static
方法无法直接访问对象实例的非静态属性。
Object
类定义了许多通用方法,如 toString()
、equals()
等。继承自 Object
的类不需要强制重写 toString()
和 equals()
方法,但通常建议重写以提供更有用的实现。
使用 final
定义的变量必须在声明时或者在构造方法中初始化
abstract class QAnimal{
public int distance = 0;
int id;
static int total = 0;
public QAnimal(){
id = total++;
}
public void move(int d){
System.out.println(this + "方式:运动");
}
public String toString(){
return "运动距离" + distance+",";
}
}
class QBird extends QAnimal{
public void move(int d){
distance = d;
System.out.println(this + "方式:飞行");
}
public String toString(){
return id + "号小鸟" + super.toString();
}
}
class QTurtle extends QAnimal{
public void move(double s){
distance += (int)s;
System.out.println(this);
}
public String toString(){
return id + "号海龟" + super.toString();
}
}
public class T24B {
public static void main(String[] args){
QBird[] blist = {new QBird(),new QBird()};
QTurtle t = new QTurtle();
QAnimal[] alist = {blist[0],t,blist[1]};
for (QAnimal a : alist) a.move(100);
t.move(15.0);
}
}
答案:(打印this会调用tostring)
0号小鸟运动距离100,方式:飞行
2号海龟运动距离0,方式:运动
1号小鸟运动距离100,方式:飞行
2号海龟运动距离15,
递归的某些读程序题关键点在于读懂方法的作用,层层递归太多的话很容易弄错,也容易弄不完
public static
和 static public
是等价的,但通常习惯写成 public static
。
main
方法必须声明为 public static void main
,并且接受一个字符串数组作为参数。
接口不继承 Object
类,但实现接口的类继承 Object
类。
如果类没有重写 toString()
方法,则使用 Object
类的 toString()
方法,默认返回对象的内存地址。
写一个高精度减法,方法头如下:public static String sub(String a, String b) ,其中a,b分别为被减数和减数,要求返回一个字符串表示的结果。如:sub(“123456789”,“1”) 返回为“123456788”;sub(“123456788”,“123456789”)返回为“-1”。需要注意:数字位数可能比较多。不能使用现成的如BigInteger、BigDecimal类实现(10分)
public class HighPrecisionSubtraction {
public static String sub(String a, String b) {
// 判断a, b哪个大,若a < b, 结果为负数
boolean isNegative = false;
if (compare(a, b) < 0) {
// 交换a和b,保证a >= b
String temp = a;
a = b;
b = temp;
isNegative = true;
}
// 将a和b填充至相同的长度
int lenA = a.length();
int lenB = b.length();
int maxLen = Math.max(lenA, lenB);
a = padLeft(a, maxLen);
b = padLeft(b, maxLen);
// 结果的存储,最大可能需要lenA+1位
StringBuilder result = new StringBuilder();
int borrow = 0;
// 从低位到高位进行逐位减法
for (int i = maxLen - 1; i >= 0; i--) {
int digitA = a.charAt(i) - '0'; // 被减数的当前位
int digitB = b.charAt(i) - '0'; // 减数的当前位
// 进行减法,考虑借位
int diff = digitA - digitB - borrow;
if (diff < 0) {
diff += 10;
borrow = 1; // 发生借位
} else {
borrow = 0;
}
result.append(diff);
}
// 反转结果,去掉前导零
result.reverse();
// 去掉可能出现的前导零
while (result.length() > 1 && result.charAt(0) == '0') {
result.deleteCharAt(0);
}
// 如果结果是负数,添加负号
if (isNegative) {
result.insert(0, '-');
}
return result.toString();
}
// 比较两个字符串表示的数字,返回负数(a < b)、零(a == b)或正数(a > b)
private static int compare(String a, String b) {
// 先比较长度,长度较大的数字较大
if (a.length() > b.length()) return 1;
if (a.length() < b.length()) return -1;
// 长度相同,逐位比较
for (int i = 0; i < a.length(); i++) {
if (a.charAt(i) > b.charAt(i)) return 1;
if (a.charAt(i) < b.charAt(i)) return -1;
}
return 0; // 相等
}
// 将数字字符串填充至指定长度,前面补0
private static String padLeft(String str, int length) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length - str.length(); i++) {
sb.append('0');
}
sb.append(str);
return sb.toString();
}
// 测试
public static void main(String[] args) {
System.out.println(sub("123456789", "1")); // 123456788
System.out.println(sub("123456788", "123456789")); // -1
System.out.println(sub("500", "200")); // 300
System.out.println(sub("1000", "999")); // 1
System.out.println(sub("5000", "10000")); // -5000
}
}
deleteDuplicates
方法
此方法删除链表中所有重复的节点,使得每个节点的值只出现一次。
public ListNode deleteDuplicates(ListNode head) {
if (head == null) {
return null; // 如果链表为空,直接返回
}
ListNode current = head;
while (current != null) {
ListNode runner = current;
// 删除当前节点之后所有重复的节点
while (runner.next != null) {
if (runner.next.data == current.data) {
runner.next = runner.next.next; // 删除重复节点
} else {
runner = runner.next; // 继续查找
}
}
current = current.next; // 继续处理下一个节点
}
return head;
}
hasCycle
方法
此方法判断链表中是否存在环。如果链表中存在环,返回 true
;否则,返回 false
。
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false; // 链表为空或只有一个节点,肯定没有环
}
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next; // 慢指针每次走一步
fast = fast.next.next; // 快指针每次走两步
if (slow == fast) {
return true; // 快慢指针相遇,说明链表有环
}
}
return false; // 快指针走到链表末尾,没有环
}
class Element {
private String s;
public Element(String text) { s = text; }
public String toString() { return s; }
}
class B extends Element {
protected final Element sub;
public B(Element e) { super(""); sub = e; }
public String toString() {
return "" + sub.toString() + "";
}
}
class I extends B {
public I(Element e) { super(e); }
public String toString() {
return "" + sub.toString() + "";
}
}
public class T24 {
public static void main(String[] args) {
Element e = new Element("OK"), i = new I(e), b = new B(i);
System.out.println( e +"\n" + i +"\n" + b +"\n" + new I(b)); }
}
答案:
OK
OK
OK
OK
double
类型而不是 float
类型char ca = 'r'
;