A24计科6班-罗坤
知识点
下面是一个简单的HelloWorld.java程序,展示了Java程序的基本结构和注释的使用。
/**
* 这是一个简单的Java程序,用于输出"Hello, World!"。
*/
public class HelloWorld {
/**
* 程序的主方法,执行程序的入口。
* @param args 命令行参数
*/
public static void main(String[] args) {
// 输出 Hello, World! 到控制台
System.out.println("Hello, World!");
}
}
HelloWorld.java
。HelloWorld.java
所在的目录。javac HelloWorld.java
。java HelloWorld
。Hello, World!
。通过这一章,我能够:
class
、public
等是关键字,不能作为标识符使用;保留字是为Java未来版本预留的关键字。_
或美元符号$
开头。byte
、short
、int
、long
float
、double
char
boolean
DataType VariableName = Value;
int age = 20;
、double price = 19.95;
final
关键字。final double PI = 3.14159;
// 定义变量与常量
public class VariablesAndConstants {
public static void main(String[] args) {
// 变量定义与赋值
int studentNumber = 100;
double averageScore = 85.5;
char grade = 'A';
// 常量定义
final double PI = 3.14159;
final int MAX_STUDENTS = 200;
// 打印变量与常量
System.out.println("Student Number: " + studentNumber);
System.out.println("Average Score: " + averageScore);
System.out.println("Grade: " + grade);
System.out.println("PI: " + PI);
System.out.println("Maximum Students: " + MAX_STUDENTS);
}
}
Student Number: 100
Average Score: 85.5
Grade: A
PI: 3.14159
Maximum Students: 200
刚开始学习变量和常量的时候,对标识符的命名规则有些困惑,尤其是哪些字符可以作为开头,哪些不可以。通过记忆和练习,逐渐掌握了驼峰命名法,并且理解了变量和常量在程序中的不同用途。赋值时需要注意数据类型的匹配,避免类型不兼容的问题。
+
、-
、*
、/
、%
(取余)int sum = 10 + 5;
、int remainder = 10 % 3;
→ remainder = 1
=
、+=
、-=
、*=
、/=
、%=
等。int a = 5;
、a += 3;
→ a = 8
==
、!=
、>
、<
、>=
、<=
boolean isEqual = (10 == 20);
→ isEqual = false
&&
(与)、||
(或)、!
(非)!(10 > 5) || (20 < 30)
→ true
&
、|
、^
(异或)、~
(取反)、<<
(左移)、>>
(右移)? :
用于根据条件选择两个表达式中的一个。int max = (a > b) ? a : b;
// 运算符与表达式的使用
public class OperatorsAndExpressions {
public static void main(String[] args) {
int a = 10, b = 20;
double c = 15.5;
// 算术运算符
System.out.println("a + b = " + (a + b));
System.out.println("a - b = " + (a - b));
System.out.println("a * c = " + (a * c));
System.out.println("b / a = " + (b / (double)a));
System.out.println("b % a = " + (b % a));
// 赋值运算符
a += 5; // a = a + 5 → 15
System.out.println("a after += 5: " + a);
// 关系运算符
boolean isGreater = (a > b); // false
System.out.println("Is a > b? " + isGreater);
// 逻辑运算符
boolean condition = (a > 5) && (c < 20); // true
System.out.println("Condition: " + condition);
// 三元运算符
int max = (a > b) ? a : b;
System.out.println("Max of a and b: " + max);
}
}
a + b = 30
a - b = -10
a * c = 155.0
b / a = 2.0
b % a = 10
a after += 5: 15
Is a > b? false
Condition: true
Max of a and b: 20
运算符是编程中的基础,理解每种运算符的用法和优先级非常重要。刚开始的时候,我对位运算符不太熟悉,但通过练习和查阅资料,逐渐掌握了它们的使用场景。特别是三元运算符,虽然简洁,但在某些情况下可以替代if语句,使代码更加优雅。不过,需要注意的是,三元运算符不应嵌套使用,否则会降低代码的可读性。
1、if语句:
if (条件) {代码块}
if (条件) {代码块} else {代码块}
if else if
链用于多个条件判断。if (score > 90) {
grade = 'A';
} else if (score > 80) {
grade = 'B';
} else {
grade = 'C';
}
2、switch语句:
switch (表达式) {
case 值1:
代码块1;
break;
case 值2:
代码块2;
break;
...
default:
代码块n;
}
例如:
switch (day) {
case "Monday":
System.out.println("开始新的一周");
break;
case "Friday":
System.out.println("即将周末");
break;
default:
System.out.println("普通的一天");
}
3、嵌套选择结构:
示例代码:
// 选择结构示例
public class Selection Structures {
public static void main(String[] args) {
int score = 85;
char grade;
// if else if 结构
if (score > 90) {
grade = 'A';
} else if (score > 80) {
grade = 'B';
} else if (score > 70) {
grade = 'C';
} else if (score > 60) {
grade = 'D';
} else {
grade = 'F';
}
System.out.println("Grade: " + grade);
// switch结构
String day = "Friday";
switch (day) {
case "Monday":
System.out.println("开始新的一周");
break;
case "Tuesday":
case "Wednesday":
case "Thursday":
System.out.println("工作日");
break;
case "Friday":
System.out.println("即将周末");
break;
case "Saturday":
case "Sunday":
System.out.println("周末");
break;
default:
System.out.println("未知的日期");
}
}
}
运行结果:
Grade: B
即将周末
选择结构是控制程序流程的关键工具。if语句简单直接,但当条件较多时,使用if else if链可以保持代码的清晰。switch语句在处理枚举或已知的固定值时非常方便,但需要注意每个case后的break,避免程序流向下执行。嵌套选择结构虽然强大,但容易使逻辑复杂化,影响代码的可维护性。在实际开发中,应尽量简化逻辑,避免过多的嵌套。
1、for循环:
for (初始化表达式; 循环条件; 更新表达式) {代码块}
for (int i = 0; i < 5; i++) {
System.out.println("i = " + i);
}
2、while循环:
while (条件) {代码块}
while (balance > 0) {
withdraw(10);
balance -= 10;
}
3、do...while循环:
do {代码块} while (条件);
do {
System.out.println("至少执行一次");
} while (false);
4、循环控制语句:
break
:跳出当前循环。continue
:跳过当前迭代,继续下一次循环。for (int i = 0; i < 10; i++) {
if (i == 5) continue;
System.out.println(i);
}
运行结果会跳过5,打印0到9,除了5。
5、嵌套循环:
示例代码:
// 循环结构示例
public class Loops {
public static void main(String[] args) {
// for循环
for (int i = 1; i <= 10; i++) {
System.out.println("Number: " + i);
}
// while循环
int j = 1;
while (j <= 10) {
System.out.println("Number: " + j);
j++;
}
// do...while循环
int k = 1;
do {
System.out.println("Number: " + k);
k++;
} while (k <= 10);
// 嵌套循环
for (int row = 1; row <= 3; row++) {
for (int col = 1; col <= 3; col++) {
System.out.print(row * col + " ");
}
System.out.println();
}
}
}
运行结果:
Number: 1
Number: 2
...
Number: 10
Number: 1
Number: 2
...
Number: 10
Number: 1
Number: 2
...
Number: 10
1 2 3
2 4 6
3 6 9
循环结构是编程中非常重要的部分,用于重复执行任务。for循环适合已知循环次数的情况,而while和do...while则适用于未知次数的循环。理解不同循环的适用场景,能够帮助我们写出更高效、更可读的代码。嵌套循环虽然强大,但容易导致逻辑复杂,因此在使用时应谨慎,确保每层循环的条件和控制变量清晰明了。此外,break
和continue
是控制循环流程的重要工具,但过多使用可能会降低代码的可维护性,应合理使用。
1、方法的定义:
修饰符 返回类型 方法名(参数列表) {方法体}
public static void greet(String name) {
System.out.println("Hello, " + name);
}
2、方法的调用:
greet("Luo");
→ Hello, Luo
3、方法重载:
public static void greet() {
System.out.println("Hello, Stranger");
}
public static void greet(String name) {
System.out.println("Hello, " + name);
}
4、递归方法(Recursive Method):方法调用自身。
public static int factorial(int n) {
if (n <= 1) return 1;
else return n * factorial(n - 1);
}
5、可变参数(Varargs):允许方法接受不定数量的参数。
修饰符 返回类型 方法名(参数类型 ... 参数名) {方法体}
public static void printNumbers(int... numbers) {
for (int number : numbers) {
System.out.print(number + " ");
}
}
6、返回值:
return
语句。return x + y;
示例代码
// 方法示例
public class Methods {
// 定义一个无参数的方法
public static void greet() {
System.out.println("Hello, Stranger");
}
// 定义一个有参数的方法
public static void greet(String name) {
System.out.println("Hello, " + name);
}
// 定义一个有返回值的方法
public static int add(int a, int b) {
return a + b;
}
// 定义一个可变参数的方法
public static void printNumbers(int... numbers) {
for (int number : numbers) {
System.out.print(number + " ");
}
System.out.println();
}
public static void main(String[] args) {
greet(); // 调用无参数方法
greet("Luo"); // 调用有参数方法
int sum = add(5, 10); // 调用有返回值的方法
System.out.println("Sum: " + sum);
printNumbers(1, 2, 3, 4, 5); // 调用可变参数的方法
}
}
运行结果:
Hello, Stranger
Hello, Luo
Sum: 15
1 2 3 4 5
方法是组织和重用代码的基本单元。通过定义方法,我们可以将复杂的任务分解成更小、更易管理的部分。方法重载增加了方法的灵活性,使我们能够用不同的参数来执行类似的操作。递归方法在处理某些问题时非常有用,如阶乘和树结构的遍历,但需要注意基例的设定,以避免无限递归。可变参数使得方法能够接受不定数量的参数,增加了方法的通用性。理解方法的定义、调用和返回值的处理,是编写结构化程序的基础。
1、数组的声明与初始化:
DataType[] arrayName;
或 DataType arrayName[];
int[] numbers = {1, 2, 3, 4, 5};
int[] numbers = new int[5];
然后逐个赋值。2、数组的长度:
arrayName.length
返回数组的长度。3、数组的遍历:
for (int i = 0; i < arrayName.length; i++) { System.out.println(arrayName[i]); }
for (DataType element : arrayName) { System.out.println(element); }
4、二维数组:
DataType[][] arrayName;
int[][] matrix = { {1, 2}, {3, 4}, {5, 6} };
5、数组的排序:
Arrays.sort(arrayName);
对数组进行排序。6、数组的复制:
System.arraycopy(src, srcPos, dest, destPos, length);
进行数组复制。7、数组的搜索:
Arrays.binarySearch(sortedArray, key);
进行二分查找。// 数组示例
public class Arrays {
public static void main(String[] args) {
// 一维数组的声明与初始化
int[] numbers = {1, 2, 3, 4, 5};
System.out.println("First element: " + numbers[0]);
numbers[1] = 10; // 修改元素
System.out.println("Modified second element: " + numbers[1]);
System.out.println("Length of array: " + numbers.length);
// 遍历数组
for (int number : numbers) {
System.out.print(number + " ");
}
System.out.println();
// 二维数组的声明与初始化
int[][] matrix = { {1, 2}, {3, 4}, {5, 6} };
System.out.println("Matrix elements:");
for (int[] row : matrix) {
for (int element : row) {
System.out.print(element + " ");
}
System.out.println();
}
}
}
运行结果:
First element: 1
Modified second element: 10
Length of array: 5
1 10 3 4 5
Matrix elements:
1 2
3 4
5 6
数组是存储和管理数据的基本数据结构,特别适用于存储相同类型的大量数据。理解数组的声明、初始化和索引的使用,是处理集合数据的基础。遍历数组是常见的操作,无论是使用传统的for循环还是增强for循环,都需要熟练掌握。二维数组在处理表格或矩阵数据时非常有用,但需要注意嵌套循环的逻辑。数组的排序和搜索方法,如Arrays.sort
和Arrays.binarySearch
,提供了高效的工具,但需要理解其背后的算法原理,以便在需要时能够手动实现。
1、栈内存的特点:
生命周期与方法调用相关,方法执行完毕后,栈帧被销毁。
存取速度快,因为栈的存取是线性的。
栈溢出(StackOverflow):当方法调用层级过多或局部变量过多时发生。
2、堆内存的特点:
生命周期长,对象在堆中分配,直到不再被引用时被垃圾回收。
存取速度较栈慢,因为堆的存取是非线性的。
内存泄漏(Memory Leak):当对象不再被使用但未被正确回收时发生。
3、对象的内存分配:
使用new
关键字在堆中分配对象。
对象的引用变量存储在栈中,指向堆中的对象。
4、数组的内存分配:
数组本质上也是对象,因此数组的元素存储在堆中。
数组的引用变量存储在栈中。
// 堆和栈的示例
public class HeapAndStack {
public static void main(String[] args) {
// 栈内存:局部变量
int a = 10;
int b = 20;
// 堆内存:对象和数组
int[] numbers = new int[5];
numbers[0] = 1;
numbers[1] = 2;
numbers[2] = 3;
numbers[3] = 4;
numbers[4] = 5;
// 打印堆和栈的信息
System.out.println("a: " + a); // 栈中的变量
System.out.println("numbers: " + numbers); // 堆中的数组
System.out.println("First element of numbers: " + numbers[0]);
}
}
运行结果
a: 10
numbers: [I@15db9742
First element of numbers: 1
理解JVM中的堆和栈内存对于编写高效的Java程序至关重要。栈内存主要用于存储局部变量和方法调用的上下文,访问速度快,但生命周期较短。堆内存用于存储对象和数组,由垃圾回收器管理,访问速度相对较慢。需要注意的是,虽然局部变量存储在栈中,但它们引用的对象却存储在堆中。这种分离意味着即使局部变量的生命周期结束,堆中的对象仍然存在,直到没有引用指向它们时才会被回收。这种内存管理机制有助于防止内存泄漏,但也需要程序员谨慎管理对象的引用,确保不再使用的对象能够被正确回收。
通过这一章的学习,我对Java的基本编程概念有了更深入的理解。变量和常量的使用、不同的运算符、选择和循环结构、方法的定义与调用、数组的操作,以及JVM中的内存管理,都是构建复杂程序的基础。在学习过程中,我遇到了一些难点,比如运算符的优先级、方法的重载与递归、以及堆和栈内存的区别。通过大量的练习和查阅资料,我逐步克服了这些困难,提高了我的编程能力和问题解决技巧。接下来,我将继续深入学习Java的面向对象特性,进一步巩固我的编程基础。
// 定义一个 Student 类
public class Student {
// 属性
String name;
int age;
// 行为
public void study() {
System.out.println(name + " is studying.");
}
public void eat() {
System.out.println(name + " is eating.");
}
}
// 创建 Student 对象
public class Main {
public static void main(String[] args) {
Student stu1 = new Student();
stu1.name = "Alice";
stu1.age = 20;
stu1.study(); // 输出: Alice is studying.
stu1.eat(); // 输出: Alice is eating.
}
}
刚开始接触面向对象的概念时,对类和对象的区别有些模糊。通过编写简单的Student类并创建对象,逐渐理解了类是抽象的模板,而对象是具体的实例。封装、继承、多态这三个特性如何在实际开发中应用,还需要进一步深入学习。
class
关键字定义类,包括类名、属性和方法。new
关键字实例化对象。class ClassName { /* members */ }
。ClassName objectName = new ClassName();
。// 定义一个 Student 类,包含构造方法
public class Student {
String name;
int age;
// 构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void introduce() {
System.out.println("My name is " + name + " and I'm " + age + " years old.");
}
}
// 创建 Student 对象
public class Main {
public static void main(String[] args) {
Student stu1 = new Student("Bob", 22);
stu1.introduce(); // 输出: My name is Bob and I'm 22 years old.
}
}
在这一部分,我深入理解了类的定义和对象的创建过程。通过引入构造方法,能够更方便地初始化对象的属性。匿名对象的概念有些复杂,但通过练习,我逐渐掌握了它的使用场景。对象在内存中的存储方式也是重要的知识点,理解栈和堆的区别有助于编写高效的程序。
ClassName([params]) { /* initialization code */ }
。this
关键字在构造方法中的应用,用于区分同名变量。public class Rectangle {
double length;
double width;
// 无参数的构造方法
public Rectangle() {
length = 0;
width = 0;
}
// 有参数的构造方法
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
public double area() {
return length * width;
}
}
public class Main {
public static void main(String[] args) {
Rectangle rect1 = new Rectangle();
System.out.println("Area: " + rect1.area()); // 输出: Area: 0.0
Rectangle rect2 = new Rectangle(5.0, 3.0);
System.out.println("Area: " + rect2.area()); // 输出: Area: 15.0
}
}
构造方法的重载让我能够用不同的方式初始化对象,这在实际开发中非常有用。this
关键字的使用一开始有些混乱,但通过多练习,我学会了如何正确地用它来区分成员变量和局部变量。理解构造方法如何与对象的生命周期结合,有助于编写更加清晰和高效的代码。
this
关键字this
关键字: 代表当前对象的引用,用于访问当前对象的成员变量和方法。this
用于区分同名的成员变量和局部变量。this
可以调用当前对象的其他方法或构造方法。this
不能在静态方法中使用,因为静态方法不属于任何对象。public class Person {
String name;
public Person(String name) {
this.name = name;
}
public void sayHello() {
System.out.println("Hello, my name is " + this.name);
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person("Kimi");
person.sayHello(); // 输出: Hello, my name is Kimi
}
}
this
关键字的使用一开始有些难以理解,特别是何时需要使用它。通过编写代码,我发现this
在有同名变量时非常有用,能够清晰地区分成员变量和局部变量。此外,this
还可以用于调用当前对象的其他方法,增强了代码的复用性。需要注意的是,this
不能在静态方法中使用,因为静态方法不依赖于对象实例。
static
关键字static
关键字: 用于定义类的静态变量、静态方法和静态代码块。public class Bank {
// 静态变量
static int totalAccounts = 0;
// 实例变量
String accountHolder;
double balance;
public Bank(String accountHolder, double balance) {
this.accountHolder = accountHolder;
this.balance = balance;
totalAccounts++;
}
// 静态方法
public static void showTotalAccounts() {
System.out.println("Total accounts: " + totalAccounts);
}
}
public class Main {
public static void main(String[] args) {
Bank bank1 = new Bank("Alice", 1000.0);
Bank bank2 = new Bank("Bob", 1500.0);
Bank.showTotalAccounts(); // 输出: Total accounts: 2
}
}
static
关键字让我能够定义属于类而不是对象的变量和方法。这在需要共享数据或提供工具方法时非常有用。静态变量的共享性质意味着它们在所有对象之间保持一致,这在计数或缓存等场景中非常有用。然而,过多使用静态变量和方法可能会使代码难以测试和维护,因此需要谨慎使用。
package packageName;
。import packageName.ClassName;
或 import packageName.*;
。java.lang
、java.util
等。com.companyname.projectname
。假设我们有两个包com.school
和com.school.students
。
在com.school
包中有一个Teacher
类:
package com.school;
public class Teacher {
public void teach() {
System.out.println("Teacher is teaching.");
}
}
在com.school.students
包中有一个Student
类:
package com.school.students;
import com.school.Teacher;
public class Student {
public void studyWithTeacher(Teacher teacher) {
teacher.teach();
System.out.println("Student is studying with the teacher.");
}
}
public class Main {
public static void main(String[] args) {
Teacher teacher = new Teacher();
Student student = new Student();
student.studyWithTeacher(teacher);
// 输出:
// Teacher is teaching.
// Student is studying with the teacher.
}
}
包的概念一开始有些抽象,但通过实际操作,我理解了它在组织和管理类方面的重要性。合理地使用包能够避免类名冲突,使项目结构更加清晰。导入包的机制让我能够方便地使用其他包中的类,但需要注意避免循环依赖和过多的导入,这可能会导致项目结构混乱。
通过第三章的学习,我系统地掌握了面向对象程序设计的基础知识,包括类与对象的概念、构造方法的使用、this
和static
关键字的应用,以及包的管理。这些知识点不仅加深了我对Java语言的理解,也为后续更复杂面向对象特性的学习打下了坚实的基础。在学习过程中,我遇到了一些理解上的困难,特别是this
和static
的使用场景,但通过大量的练习和实例编写,我逐渐克服了这些难点,能够更加自信地应用这些概念进行编程。
private
、protected
、public
,以及默认(无修饰符)。private
:仅在类内部可访问。protected
:在类及其子类中可访问。public
:在任何地方都可访问。get
开头,后跟属性名,首字母大写,如getName()
。set
开头,后跟属性名,首字母大写,如setName(name)
。public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age > 0 && age < 200) { // 验证年龄的合理性
this.age = age;
} else {
System.out.println("年龄不合法");
}
}
}
封装让我意识到隐藏类的内部细节的重要性。通过提供getter和setter方法,不仅保护了数据,还使得类的接口更加清晰。然而,我一开始对访问修饰符的使用有些困惑,特别是protected
和默认修饰符的区别。通过查阅资料和练习,我逐渐理解了它们在不同场景下的应用。难点在于理解protected
修饰符的使用范围。最初,我不清楚protected
是否能被其他包中的子类访问。通过查阅Java文档和做实验,我发现protected
确实允许子类访问,即使它们在不同的包中。这帮助我明确了不同访问修饰符的适用场景。
extends
。class People {
protected String name;
protected int age;
public People(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("人会吃东西");
}
}
class Teacher extends People {
public Teacher(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("老师会吃东西,但可能更健康");
}
public void teach() {
System.out.println("老师会教学");
}
}
class Worker extends People {
public Worker(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("工人会吃东西,可能更实在");
}
public void work() {
System.out.println("工人会工作");
}
}
继承是面向对象编程的核心之一。它通过extends
关键字实现,使得子类可以复用父类的代码,同时添加新的功能。方法重写让我看到了多态性的初步面貌,即同一个方法名在不同类中可以有不同的行为。我一开始对方法重写的规则有些模糊,特别是关于访问权限和返回类型的部分。通过编写示例代码并运行,我逐步理解了这些规则的实际应用。在方法重写时,我曾困惑于如何正确地重写父类的方法,尤其是当父类方法有多个重载时。我通过查阅资料和反复编写测试代码,最终掌握了方法重写的细节。此外,理解单继承和多层继承的区别也花了一些时间,但通过类比和练习,我逐渐理清了思路。
super()
调用父类的构造方法。super.methodName()
调用父类的方法。this
指向当前对象,可以访问当前类的所有成员。super
指向父类对象,只能访问父类的成员,不能访问子类的成员。super
不能在static方法中使用,因为static方法不与对象实例关联。super
只能访问直接父类的成员,不能访问更高级的父类。class Teacher extends People {
public Teacher(String name, int age) {
super(name, age); // 调用父类的构造方法
}
public void eat() {
super.eat(); // 调用父类的eat方法
System.out.println("老师会吃东西,但可能更健康");
}
}
super
关键字在继承中扮演着重要角色,尤其是在调用父类的构造方法和重写的方法时。通过super
,子类可以方便地复用父类的实现,同时添加自己的逻辑。我一开始对super
和this
的区别有些模糊,但通过编写示例代码并观察它们的行为,我逐渐理解了它们各自的应用场景。难点在于理解super
只能访问直接父类的成员,而不能访问更高级的父类。在实际编程中,我曾尝试使用super
调用祖父类的方法,结果出现了编译错误。通过查阅资料和反复测试,我明白了super
的这一限制,从而避免了类似的错误。
1、final类的使用:
2、final方法的使用:
3、final变量的使用:
4、final的注意事项:
final
不能用于局部变量的返回类型。final
不能用于抽象方法。public final class MathUtils {
public final static double PI = 3.1415926;
public final double calculateCircleArea(double radius) {
return PI * radius * radius;
}
}
final
关键字在Java中用于定义不可变的元素。通过将类、方法或变量声明为final
,可以防止它们被继承、重写或重新赋值。我一开始对final
在不同上下文中的应用有些不确定,但通过阅读和编写示例代码,我逐渐掌握了它的用法。特别是,理解final
类和final
方法如何增强代码的稳定性和安全性,让我对Java的类型系统有了更深的认识。在使用final
时,我曾困惑于它与abstract
的关系,尤其是在定义抽象类和方法时。通过查阅资料和实际编程,我明白了final
与abstract
是互斥的,final
用于防止扩展,而abstract
用于强制子类提供实现。这一理解帮助我正确地在项目中使用这两个关键字。
toString()
、equals()
、hashCode()
等。toString()
: 返回对象的字符串表示。equals(Object obj)
: 比较两个对象是否相等。hashCode()
: 返回对象的哈希码。toString()
以提供更有意义的字符串表示。equals()
以根据对象的属性定义相等性。equals()
,应同时重写hashCode()
以保持一致性。class People {
private String name;
private int age;
public People(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "People[name=" + name + ", age=" + age + "]";
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
People people = (People) obj;
return age == people.age && name.equals(people.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
Object
类是所有类的基类,提供了基本的方法。重写这些方法可以为我们的类提供更合适的行为。特别是,toString()
方法在调试和日志记录中非常有用,equals()
和hashCode()
则在集合框架中至关重要。我一开始对这些方法的重要性认识不足,但通过实际编写和测试,我看到了它们在实际应用中的价值。在重写equals()
和hashCode()
时,我曾对如何正确实现这些方法感到困惑。通过查阅Java文档和示例代码,我学习到了最佳实践,例如在equals()
中使用getClass()
检查类型,并在hashCode()
中使用Objects.hash()
方法。此外,理解hashCode()
的用途,特别是在哈希表中,帮助我避免了潜在的错误。
1、多态的条件:
2、多态的类型:
3、多态的注意点:
class People {
public void eat() {
System.out.println("人会吃东西");
}
}
class Teacher extends People {
@Override
public void eat() {
System.out.println("老师会吃东西,但可能更健康");
}
}
class Worker extends People {
@Override
public void eat() {
System.out.println("工人会吃东西,可能更实在");
}
}
public class PolymorphismTest {
public static void main(String[] args) {
People p1 = new Teacher();
p1.eat(); // 输出:老师会吃东西,但可能更健康
p1 = new Worker();
p1.eat(); // 输出:工人会吃东西,可能更实在
}
}
多态是面向对象编程的三大特性之一,它允许同一个接口或父类引用指向不同的子类对象,并在运行时表现出不同的行为。这极大地增强了代码的灵活性和可扩展性。我一开始对多态的内部机制有些模糊,特别是如何在运行时决定调用哪个方法。通过编写示例代码并观察输出,我理解了JVM在运行时如何处理多态。理解多态与方法重写的关系曾是我的一个难点。我曾不清楚多态是否仅限于方法重写,还是也包括方法重载。通过查阅资料和实际编程,我明确了多态主要涉及方法重写,而方法重载更多是编译时的多态。此外,理解this
和super
在多态中的作用,也帮助我更好地掌握了这一概念。
1、抽象类的使用:
abstract
关键字定义。2、抽象方法的使用:
abstract
关键字定义,没有方法体。3、抽象类与接口的区别:
extends
继承抽象类,通过implements
实现接口。public abstract class Shape {
private String name;
public Shape(String name) {
this.name = name;
}
public abstract double getArea();
public abstract double getPerimeter();
public String getName() {
return name;
}
}
class Circle extends Shape {
private double radius;
public Circle(String name, double radius) {
super(name);
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
@Override
public double getPerimeter() {
return 2 * Math.PI * radius;
}
}
class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(String name, double width, double height) {
super(name);
this.width = width;
this.height = height;
}
@Override
public double getArea() {
return width * height;
}
@Override
public double getPerimeter() {
return 2 * (width + height);
}
}
抽象类提供了一种定义接口的方式,同时可以包含一些具体的实现。通过将某些方法声明为抽象,父类可以强制子类提供特定的方法实现。这在设计具有共同属性和行为的类层次结构时非常有用。我一开始对抽象类和抽象方法的使用有些不确定,但通过编写示例代码并观察其行为,我逐渐理解了它们在实际项目中的应用。在理解抽象类与接口的关系时,我曾感到困惑。通过查阅资料和比较它们的定义与使用,我明确了抽象类可以包含部分实现,而接口更多用于定义完全抽象的规范。此外,理解抽象类在继承中的作用,帮助我更好地设计复杂的类层次结构。
implements
关键字实现。1、接口的使用:
interface
关键字定义。public abstract
。2、实现接口:
implements
关键字实现一个或多个接口。3、接口的继承:
extends
关键字。public interface USB {
void connect();
void disconnect();
default void showInfo() {
System.out.println("USB设备信息");
}
static void staticMethod() {
System.out.println("USB静态方法");
}
}
class Mouse implements USB {
@Override
public void connect() {
System.out.println("鼠标连接");
}
@Override
public void disconnect() {
System.out.println("鼠标断开连接");
}
}
class Keyboard implements USB {
@Override
public void connect() {
System.out.println("键盘连接");
}
@Override
public void disconnect() {
System.out.println("键盘断开连接");
}
}
接口提供了一种定义方法规范的方式,由实现类提供具体的实现。通过接口,可以定义一个通用的API,使得不同的实现类可以互换使用,极大地增强了代码的灵活性和可扩展性。我一开始对接口中的default
和static
方法有些困惑,但通过查阅JDK8的新特性资料,我理解了这些方法的用途和实现方式。理解接口与抽象类的区别曾是我的一个难点。通过查阅资料和比较它们的定义与使用,我明确了接口更多用于定义完全抽象的规范,而抽象类可以包含部分实现。此外,理解接口在多继承中的作用,帮助我更好地设计复杂的系统。
1、成员内部类:
2、静态内部类:
static
关键字定义。3、局部内部类:
4、匿名内部类:
public class OuterClass {
private String outerField = "外层类的字段";
class InnerClass {
public void accessOuter() {
System.out.println("访问外层类的字段: " + outerField);
}
}
static class StaticInnerClass {
public static void staticMethod() {
System.out.println("静态内部类的静态方法");
}
}
public void methodWithLocalInnerClass() {
class LocalInnerClass {
public void display() {
System.out.println("局部内部类");
}
}
LocalInnerClass local = new LocalInnerClass();
local.display();
}
public void methodWithAnonymousInnerClass(USB usb) {
usb.connect();
}
}
interface USB {
void connect();
}
class Mouse implements USB {
public void connect() {
System.out.println("鼠标连接");
}
}
内部类提供了更细粒度的封装,允许在类内部定义类,从而更好地组织和管理代码。成员内部类可以访问外部类的所有成员,这在某些情况下非常有用。静态内部类则提供了一种不需要外部类实例即可访问的途径。局部内部类和匿名内部类则在特定的上下文中提供了更大的灵活性。我一开始对内部类的使用有些不确定,特别是它们的访问权限和创建方式。通过编写示例代码并观察它们的行为,我逐渐理解了它们在实际项目中的应用。在理解匿名内部类的使用时,我曾感到困惑,特别是它们如何实现接口或继承类。通过查阅资料和编写示例代码,我明白了匿名内部类通常用于创建一个只使用一次的对象,特别是在实现接口或继承类时,它们可以提供一个简洁的语法。此外,理解局部内部类的作用范围,帮助我更好地组织方法内的代码。
通过细致地梳理第四章的每个部分,我不仅清晰地理解了每个核心概念和知识点,还通过编写和测试示例代码,加深了对这些概念的理解。在学习过程中,我遇到了一些难点,如方法重写的规则、super
和this
的区别、抽象类与接口的关系等,但通过查阅资料、编写测试代码和反复练习,我成功地克服了这些难点。这不仅增强了我的理论知识,还提高了我的实际编程能力。
toString()
、getMessage()
、printStackTrace()
。1、异常与错误的区别:
2、Throwable类的层次结构:
Throwable
是基类,Exception
和Error
都继承自Throwable
。Exception
分为检查型异常(Checked Exception)和非检查型异常(Unchecked Exception,即RuntimeException)。3、异常处理的重要性:
try {
// 可能抛出异常的代码
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("发生算术异常: " + e.getMessage());
} finally {
System.out.println("finally块总是执行");
}
异常处理是确保程序鲁棒性的关键机制。通过try-catch
结构,我们可以捕获并处理潜在的错误,防止程序意外终止。理解Throwable
类的层次结构有助于我们更好地分类和处理不同类型的异常。最初,我对于Exception
和Error
的区别有些模糊,不清楚何时应该捕获Exception
,何时应该让Error
通过。通过查阅资料和编写示例代码,我意识到Error
通常是由 JVM 抛出的,表示程序遇到了无法恢复的错误,而Exception
则是可以被程序捕获和处理的。
throw
关键字手动抛出异常。throws
关键字声明该方法可能抛出的异常。try
块包围可能抛出异常的代码,并通过catch
块捕获和处理异常。finally
块中的代码总是执行,通常用于释放资源。throw new Exception("错误信息");
public void methodName() throws IOException, SQLException { ... }
try { ... } catch (ExceptionType e) { ... } finally { ... }
public class FileProcessor {
public void readFile(String filePath) throws IOException {
if (filePath == null) {
throw new FileNotFoundException("文件路径不能为空");
}
// 读取文件的代码
}
}
public class Main {
public static void main(String[] args) {
try {
FileProcessor fp = new FileProcessor();
fp.readFile("path/to/file.txt");
} catch (FileNotFoundException e) {
System.out.println("文件未找到: " + e.getMessage());
} catch (IOException e) {
System.out.println("读取文件时发生错误: " + e.getMessage());
} finally {
System.out.println("处理完毕");
}
}
}
异常处理机制使我们能够更灵活地应对程序中的错误。通过合理使用throw
和throws
,我们可以控制异常的传播,使程序的错误处理更加集中和高效。try-catch-finally
结构不仅能够捕获和处理异常,还能确保资源的正确释放,这对于编写高质量的软件至关重要。
在使用throws
声明异常时,我曾困惑于何时应该声明Exception
,何时应该声明具体的异常类型,如IOException
或SQLException
。通过实际项目中的练习,我意识到声明具体的异常类型可以为调用者提供更明确的信息,有助于他们编写更精确的异常处理逻辑。
核心概念
1、自定义异常的步骤:
Exception
或RuntimeException
类。2、方法重写中的异常规则:
// 自定义异常
public class LoginException extends Exception {
public LoginException(String message) {
super(message);
}
}
public class UserNotFoundException extends LoginException {
public UserNotFoundException(String message) {
super(message);
}
}
public class PasswordWrongException extends LoginException {
public PasswordWrongException(String message) {
super(message);
}
}
// 使用自定义异常
public class LoginService {
public void login(String username, String password) throws LoginException {
if (username == null || password == null) {
throw new LoginException("用户名或密码不能为空");
}
if (!userExists(username)) {
throw new UserNotFoundException("用户不存在");
}
if (!verifyPassword(username, password)) {
throw new PasswordWrongException("密码错误");
}
// 登录逻辑
}
}
自定义异常可以更精确地描述应用程序中的错误情况,使错误处理更加细粒度和可读。在方法重写时,遵循异常声明的规则确保了异常处理的一致性和可预测性,避免了因异常声明不当导致的编译错误。最初,我在定义自定义异常时不确定是否应该继承Exception
还是RuntimeException
。通过查阅资料和编写示例代码,我明白了如果希望调用者必须处理异常,应该继承Exception
;如果异常是运行时异常,可以继承RuntimeException
。此外,在方法重写时,确保子类的异常声明不比父类更宽泛,是避免编译错误的关键。
通过系统地梳理第五章“异常”的内容,我不仅掌握了异常处理的基本机制,还深入理解了如何通过自定义异常和合理的方法声明来提升程序的鲁棒性和可维护性。在学习过程中,我遇到了一些关于异常声明和自定义异常使用的困惑,但通过编写示例代码和查阅相关资料,我成功地克服了这些难点,对异常处理有了更全面的认识。
1、包装类与基本类型的对应关系:
int
↔ Integer
double
↔ Double
float
↔ Float
long
↔ Long
short
↔ Short
byte
↔ Byte
char
↔ Character
boolean
↔ Boolean
2、包装类的常用方法:
parseInt(String s)
: 将字符串转换为int。valueOf(String s)
: 将字符串转换为对应的包装类对象。toString()
: 将包装类对象转换为字符串。3、自动装箱与拆箱的使用:
// 自动装箱
Integer intObj = 100; // 等同于 Integer.valueOf(100)
// 自动拆箱
int num = intObj; // 等同于 intObj.intValue()
// 字符串转换
String s = "123";
int number = Integer.parseInt(s); // 转换为int
Integer intObj2 = Integer.valueOf(s); // 转换为Integer对象
理解包装类的概念对于在需要对象的场景中使用基本类型至关重要,例如在使用泛型时。自动装箱和拆箱简化了代码,减少了显式转换的需要。然而,需要注意的是,这些操作在背后涉及对象的创建和方法调用,不当使用可能会导致性能问题。最初,我对自动装箱和拆箱的具体工作原理感到困惑,特别是它们如何与泛型等其他Java特性相互作用。通过编写示例代码并逐步测试不同的转换,我更好地理解了这些特性的无缝集成。此外,通过阅读相关文档,我了解到了这些操作的性能影响,这对于编写高效的Java应用程序至关重要。
1、BigInteger的常用方法:
add(BigInteger val)
: 加法。subtract(BigInteger val)
: 减法。multiply(BigInteger val)
: 乘法。divide(BigInteger val)
: 除法。2、BigDecimal的常用方法:
add(BigDecimal val)
: 加法。subtract(BigDecimal val)
: 减法。multiply(BigDecimal val)
: 乘法。divide(BigDecimal val, int roundingMode)
: 除法,带舍入模式。setScale(int newScale, int roundingMode)
: 设置小数点精度和舍入模式。import java.math.BigInteger;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class BigNumbersExample {
public static void main(String[] args) {
// BigInteger示例
BigInteger bigInt1 = new BigInteger("12345678901234567890");
BigInteger bigInt2 = new BigInteger("98765432109876543210");
BigInteger sum = bigInt1.add(bigInt2);
System.out.println("BigInteger相加结果: " + sum);
// BigDecimal示例
BigDecimal bigDec1 = new BigDecimal("1234567890.1234567890");
BigDecimal bigDec2 = new BigDecimal("9876543210.9876543210");
BigDecimal sumDec = bigDec1.add(bigDec2);
System.out.println("BigDecimal相加结果: " + sumDec);
// 设置精度
BigDecimal rounded = bigDec1.setScale(2, RoundingMode.HALF_UP);
System.out.println("BigDecimal设置精度: " + rounded);
}
}
BigInteger和BigDecimal是处理大数运算的强大工具,特别是在需要高精度和大范围的金融或科学计算中。理解它们的方法和使用方式,可以让我在实际项目中更加灵活和准确地处理数值运算。在使用BigDecimal时,我最初对舍入模式感到困惑,不确定何时使用哪种模式。通过查阅文档并实验不同的舍入模式,我更好地理解了它们的行为。此外,记住在创建BigDecimal时使用字符串构造函数以避免精度问题,这一点至关重要。
1、字符串的创建:
String s = "Hello";
char[] chars = {'H', 'e', 'l', 'l', 'o'}; String s = new String(chars);
byte[] bytes = {72, 101, 108, 108, 111}; String s = new String(bytes, "UTF-8");
2、字符串的常用方法:
length()
: 返回字符串的长度。charAt(int index)
: 返回指定索引处的字符。indexOf(String str)
: 返回子字符串的起始索引。replace(char old, char new)
: 替换字符。toUpperCase()
, toLowerCase()
: 转换大小写。trim()
: 去除首尾空格。split(String regex)
: 根据正则表达式拆分字符串。public class StringComparisonExample {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "World";
System.out.println("s1的长度: " + s1.length());
System.out.println("s1的第2个字符: " + s1.charAt(1));
System.out.println("s1中是否包含 'll': " + s1.contains("ll"));
System.out.println("s1替换 'e' 为 'a': " + s1.replace('e', 'a'));
System.out.println("s1转大写: " + s1.toUpperCase());
System.out.println("s1和s2连接: " + s1.concat(s2));
}
}
String类是Java中最常用和最基础的类之一。理解其不可变性非常重要,因为它影响了字符串在内存中的处理方式。掌握常用的方法可以高效地处理和操作字符串,这是任何Java应用程序的基础。最初,我对字符串的不可变性及其对内存管理的影响感到困惑。通过研究和实验,例如在循环中连接字符串,我看到了创建新字符串对象的开销。这使我意识到在处理大量字符串操作时使用StringBuilder或StringBuffer的重要性。
1、Date类:
Date()
: 创建当前日期和时间的Date对象。getTime()
: 返回自1970年1月1日以来的毫秒数。toString()
: 返回日期的字符串表示形式。2、SimpleDateFormat类:
SimpleDateFormat(String pattern)
: 使用给定的模式创建一个日期格式化器。format(Date date)
: 将Date对象格式化为字符串。parse(String source)
: 将字符串解析为Date对象。3、Calendar类:
Calendar.getInstance()
: 获取当前日期和时间的Calendar对象。set(int year, int month, int day)
: 设置日期。get(int field)
: 获取指定的日期字段,如年、月、日。add(int field, int amount)
: 在日期上添加或减去时间。import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Calendar;
public class DateTimeExample {
public static void main(String[] args) throws Exception {
// Date示例
Date date = new Date();
System.out.println("当前日期和时间: " + date.toString());
System.out.println("当前时间戳: " + date.getTime());
// SimpleDateFormat示例
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("格式化后的日期: " + sdf.format(date));
Date parsedDate = sdf.parse("2024-05-20 12:00:00");
System.out.println("解析后的日期: " + parsedDate);
// Calendar示例
Calendar calendar = Calendar.getInstance();
calendar.set(2024, Calendar.MAY, 20);
System.out.println("Calendar中的日期: " + calendar.getTime());
calendar.add(Calendar.DAY_OF_MONTH, 10);
System.out.println("10天后的日期: " + calendar.getTime());
}
}
Date和Time类在处理日期和时间时非常基础,但它们有些过时。SimpleDateFormat提供了强大的日期格式化和解析功能,而Calendar类则提供了更灵活的日期操作。了解这些类可以帮助我有效地处理与日期和时间相关的任务。
最初,我对不同日期和时间类之间的区别感到困惑,特别是Date、Time、Calendar和SimpleDateFormat之间的关系。通过查阅文档并实验不同的用法,我更好地理解了它们的互补作用。此外,记住SimpleDateFormat不是线程安全的这一点非常重要,这影响了我在多线程环境中的使用方式。
1、Math类:
Math.abs(x)
: 绝对值。Math.max(x, y)
: 最大值。Math.min(x, y)
: 最小值。Math.sqrt(x)
: 平方根。Math.random()
: 生成0.0到1.0之间的随机数。2、Random类:
Random()
: 创建一个随机数生成器。nextInt()
: 生成下一个int随机数。nextDouble()
: 生成下一个double随机数。nextBoolean()
: 生成下一个boolean随机数。3、UUID类:
UUID.randomUUID()
: 生成一个新的随机UUID。4、枚举类:
enum
关键字定义。import java.util.Random;
import java.util.UUID;
enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public class OtherClassesExample {
public static void main(String[] args) {
// Math类示例
System.out.println("绝对值: " + Math.abs(-10));
System.out.println("最大值: " + Math.max(5, 10));
System.out.println("最小值: " + Math.min(5, 10));
System.out.println("平方根: " + Math.sqrt(16));
System.out.println("随机数: " + Math.random());
// Random类示例
Random random = new Random();
System.out.println("随机int: " + random.nextInt());
System.out.println("随机double: " + random.nextDouble());
System.out.println("随机boolean: " + random.nextBoolean());
// UUID类示例
UUID uuid = UUID.randomUUID();
System.out.println("UUID: " + uuid);
// 枚举类示例
Day today = Day.MONDAY;
System.out.println("今天是: " + today);
}
}
Math类提供了一个方便的数学运算工具集,简化了各种数学计算。Random类是生成随机数的强大工具,广泛用于游戏、测试和安全领域。UUID类确保了在分布式系统中生成唯一标识符的简便性。枚举类则提供了一种类型安全的方式来表示固定的一组常量,增强了代码的可读性和维护性。最初,我对枚举类的使用感到有些困惑,特别是它们如何与类和接口一起使用。通过编写示例并探索其用法,我理解了它们在表示有限集合中的值时的价值。此外,记住Random类的线程安全问题非常重要,以避免在多线程应用程序中出现意外行为。
通过细致地梳理第六章的每个部分,我不仅清晰地理解了每个核心概念和知识点,还通过编写和测试示例代码,加深了对这些概念的理解。在学习过程中,我遇到了一些难点,如自动装箱与拆箱的工作原理、大数类的使用细节、字符串的不可变性影响以及日期时间类的多线程安全问题。通过查阅资料、编写测试代码和反复验证,我成功地克服了这些难点,为在实际项目中高效使用这些常用类打下了坚实的基础。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。