目录

  1. 1. 前言
  2. 2. 常识
  3. 3. 命令行操作
  4. 4. Hello World
  5. 5. 基础类型&运算符
    1. 5.1. Math 类
  6. 6. 方法
    1. 6.1. static
    2. 6.2. 形参和实参
    3. 6.3. 栈帧图
  7. 7. 条件语句
    1. 7.1. 返回语句
  8. 8. 递归
  9. 9. ASSN 1 - GravityCalculator
  10. 10. ASSN 2 - FooCorporation
  11. 11. 良好的代码风格
  12. 12. 循环
  13. 13. ASSN 3 - Marathon
  14. 14.
    1. 14.1. 构造函数
  15. 15. ASSN 4 - Library
    1. 15.1. Book 类实现
    2. 15.2. Library 类实现
  16. 16. 包与访问控制
    1. 16.1. 基本概念
    2. 16.2. 系统常见包
    3. 16.3. 访问控制权限
  17. 17. Java 类集框架
    1. 17.1. List
      1. 17.1.1. ArrayList
      2. 17.1.2. Vector
      3. 17.1.3. LinkedList
    2. 17.2. Queue
    3. 17.3. Set
      1. 17.3.1. HashSet
      2. 17.3.2. TreeSet
    4. 17.4. Collection 的输出
      1. 17.4.1. Iterator
      2. 17.4.2. ListIterator
      3. 17.4.3. foreach
    5. 17.5. Map
      1. 17.5.1. Map.Entry
      2. 17.5.2. HashMap
      3. 17.5.3. Hashtable
      4. 17.5.4. TreeMap
      5. 17.5.5. WeakHashMap
      6. 17.5.6. IdentityHashMap
      7. 17.5.7. 注意
  18. 18. ASSN 5 - Graphics!
    1. 18.1. part 1
    2. 18.2. part 2
  19. 19. 继承
    1. 19.1. 覆写
    2. 19.2. super()
    3. 19.3. final 关键字
    4. 19.4. 抽象类
  20. 20. 接口
  21. 21. 多态
    1. 21.1. instanceof 关键字

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

Java基础

2023/4/2 DevOps Java
  |     |   总文章阅读量:

前言

推荐课程:MIT 6.092


常识

Java 是 Sun 公司开发的一套高级编程语言,所以有一些原生 jdk 包会以 sun 开头,后面被 Oracle 收购

Java 开发方向分为三种:

  • Java SE(Java Platform Standard Edition):包含构成 Java 语言核心的类,如数据库连接、接口定义、输入/输出、网络编程
  • Java EE(Java Platform Enterprise Edition):Java SE + 开发企业级应用的类,如 EJB、Servlet、JSP、XML、事务控制
  • Java ME(Java Platform Micro Edition):被 Android 开发取代了

高级语言具有可移植性,对于 Java 而言就是可以仅提供一个 class 从而在各个安装了对应版本 java 的计算机平台上运行,这是通过 Java 自身的 JVM 虚拟机实现的

对于高级语言编写的程序有两种方式进行翻译:

  • 解释:完成解释任务的程序叫做解释器,解释器对程序进行翻译,然后执行命令
  • 编译:编译是一个单独的步骤,运行则在编译后,此时高级语言就是源代码,编译后可执行程序

Java 编写的程序既可以被编译,也可以被解释。但是 Java 的编译过程并不生成机器语言,而是生成字节码(byte code)


命令行操作

一些常用的命令行 java 命令

# 编译
javac file.java
javac -encoding UTF-8 file.java
javac -d ./out file.java

# 运行普通class
java filename

# 运行带包名class
java com.example.filename

# 指定依赖库运行,前者是unix,后者是windows
java -cp ".:lib/*" filename
java -cp ".;lib*" HelloWorld

# 运行jar包
java -jar app.jar
java -Xms512m -Xmx1024m -jar app.jar

# 传递环境变量运行jar包
java -Dfile.encoding=UTF-8 -Dspring.profiles.active=prod -jar app.jar

jar 常用命令

# 打包
jar cvf app.jar *.class

# 指定 entrypoint 程序入口
jar cvfe app.jar HelloWorld *.class

# 列出jar包目录
jar tvf app.jar

# 解压jar包
jar xvf app.jar
jar xvf app.jar BOOT-INF/classes/application.yml

# 替换jar包中某个文件
jar uvf app.jar BOOT-INF/classes/application.yml

Hello World

编写一个 1.java

class Hello{
	public static void main(String[] arguments){
	System.out.println("Hello World!");
	}
}

然后命令行执行 javac 1.java 进行编译,可以发现生成了一个 Hello.class

这就是编译出来的 Hello 类,输入 java Hello 即可执行


基础类型&运算符

boolean
int
double
String

运算符:=、+、-、*、/、(、)、%

不同类型之间的 + 运算:任何类型的数据都向 String 转型

String text = "hello" + " world";
text = text + " number " + 5;
// text = "hello world number 5"

注意 / 在 int 和 double 下会产生不同的运算结果:

double a = 5.0/2.0; // a = 2.5 
int b = 4/2; // b = 2 
int c = 5/2; // c = 2 
double d = 5/2; // d = 2.0

类型转换:

int a = 2; // a = 2
double a = 2; // a = 2.0 (Implicit)

int a = 18.7; // ERROR
int a = (int)18.7; // a = 18

double a = 2/3; // a = 0.0
double a = (double)2/3; // a = 0.6666…

Math 类

使用 Math 类下的方法进行数学计算

Math.sin(x)
Math.cos(Math.PI / 2)
Math.pow(2, 3)
Math.log(Math.log(x + y))

方法

public static void NAME(TYPE NAME) {
	STATEMENTS
}

NAME(EXPRESSION);

static

  • 使用 static 声明属性,则此属性为全局属性
  • 使用 static 声明方法,则此属性可被称为“类方法”,可以由类名称直接调用

形参和实参

public static void printTwice(String s) {	// 此处的 s 是该方法的形参
	System.out.println(s);
	System.out.println(s);
}

printTwice("Never gonna give you up.");	// 此处调用方法传入实参

栈帧图

方法的形参和变量只在该方法内部有效

为了跟踪变量属于哪个方法,可以画栈帧图,以上面的示例为例子,栈图如下:

对于每一个方法,都有其对应的方框来包含该方法中的形参和变量,这样的方框叫做


条件语句

x > y: x is greater than y
x < y: x is less than y
x >= y: x is greater than or equal to x
x <= y: x is less than or equal to y
x == y: x equals y

返回语句

允许我们在一个方法执行完之前就使用 return 结束该方法的执行,比如在错误的时候使用返回语句:

public static void printLogarithm(double x) {
	if (x <= 0.0) {
		System.out.println("numbers should be greater than 0");
		return;
	}
	double result = Math.log(x);
	System.out.println(result);
}

如果 x <= 0 则会打印出错信息并使用返回语句结束该方法的执行,即使方法中还有没执行的代码


递归

public static void countdown(int n) {
	if (n == 0) {
		System.out.println("Blastoff!");
	} else {
		System.out.println(n);
		countdown(n-1);
	}
}
countdown(3);

ASSN 1 - GravityCalculator

Compute the position of a falling object:

class GravityCalculator {
    public static void main(String[] arguments) {
        double gravity = -9.81;  // Earth's gravity in m/s^2
        double initialVelocity = 0.0;
        double fallingTime = 10.0;
        double initialPosition = 0.0;
        double finalPosition = 0.0;
        finalPosition = 0.5 * gravity * fallingTime * fallingTime + initialVelocity * fallingTime + initialPosition
        System.out.println("The object's position after " + fallingTime +
                " seconds is " + finalPosition + " m.");
    }
}

ASSN 2 - FooCorporation

对于前 40 小时内的工作,员工按“工作时长 × 基本工资”获得报酬。
对于超过 40 小时的部分,每小时的加班费为“基本工资 × 1.5”。
基本工资不得低于最低工资标准(每小时 8.00 美元);若低于此标准,程序需输出错误提示。
若工作时长超过 60 小时,程序需输出错误信息。

请编写一个方法,接收基本工资和工作时长作为参数,并输出总薪资或错误信息。同时编写一个主方法(main method),针对以下每位员工调用该方法:

Base Pay Hours Worked
Employee 1 $7.50 35
Employee 2 $8.20 47
Employee 3 $10.00 73
class FooCorporation{
    public static void main(String[] args) {
        double Employee1Pay = calculatePay(35, 7.5);
        double Employee2Pay = calculatePay(47, 8.2);
        double Employee3Pay = calculatePay(73, 10.0);
        System.out.println("Employee 1 Pay: $" + Employee1Pay);
        System.out.println("Employee 2 Pay: $" + Employee2Pay);
        System.out.println("Employee 3 Pay: $" + Employee3Pay);
    }

    public static double calculatePay(double hoursWorked, double basePay) {
        if (basePay < 8.0) {
            return -1;
        }
        if (hoursWorked > 60) {
            return -1;
        }
        double regularHours = Math.min(hoursWorked, 40);
        double overtimeHours = Math.max(hoursWorked - 40, 0);
        double regularPay = regularHours * basePay;
        double overtimePay = overtimeHours * basePay * 1.5;
        return regularPay + overtimePay;
    }
}

良好的代码风格

  1. 使用有意义的名称
  2. 使用缩进
  3. 使用空格
  4. 不要重复条件测试
if (basePay < 8.0) {
	...
} else if (hours > 60) {
	...
} else if (basePay >= 8.0 && hours <= 60){
	...
}
// 第三个判断应直接使用 else

循环

while 和 for 循环

while (condition){
	statements
}

for (initialization;condition;update){
	statements
}

注意:如果 for 循环后面没有使用 {},那么循环体只会包含紧跟在它后面的第一条语句

for (int i=0;i<5;i++)
    System.out.println("Hi");
System.out.println("Bye");


// 输出结果如下
/*
Hi
Hi
Hi
Hi
Hi
Bye
*/

ASSN 3 - Marathon

编写一个方法,接收一个整数数组作为输入,并返回用时最短的人员对应的索引。对该用时数组调用此方法,并打印出与返回索引对应的人员姓名及用时。编写第二个方法来找出成绩第二好的跑步者;该方法应先调用第一个方法确定成绩最好的跑步者,然后遍历所有数值,找出第二好(即第二短)的用时。

class Marathon {
    public static void main (String[] arguments) {
        String[] names = {
            "Elena", "Thomas", "Hamilton", "Suzie", "Phil", "Matt", "Alex",
            "Emma", "John", "James", "Jane", "Emily", "Daniel", "Neda",
            "Aaron", "Kate"
        };

        int[] times = {
            341, 273, 278, 329, 445, 402, 388, 275, 243, 334, 412, 393, 299,
            343, 317, 265
        };

        for (int i = 0; i < names.length; i++) {
            System.out.println(names[i] + ": " + times[i]);
        }
        
        System.out.println("The fastest runner is " + names[getFastestRunnerIndex(times)] + " with a time of " + times[getFastestRunnerIndex(times)] + " minutes.");
        System.out.println("The second fastest runner is " + names[getSecondFastestRunnerIndex(times)] + " with a time of " + times[getSecondFastestRunnerIndex(times)] + " minutes.");

    }

    public static int getFastestRunnerIndex(int[] times) {
        int fastestIndex = 0;
        for (int i = 1; i < times.length; i++) {
            if (times[i] < times[fastestIndex]) {
                fastestIndex = i;
            }
        }
        return fastestIndex;
    }

    public static int getSecondFastestRunnerIndex(int[] times) {
        int fastestIndex = getFastestRunnerIndex(times);
        int secondFastestIndex = -1;
        for (int i = 0; i < times.length; i++) {
            if (i != fastestIndex) {
                if (secondFastestIndex == -1 || times[i] < times[secondFastestIndex]) {
                    secondFastestIndex = i;
                }
            }
        }
        return secondFastestIndex;
    }
}

此处对于第二名的查找,是在判断排除了第一名的情况下遍历整个数组实现的


在定义一个类时,也创建了一个同名的对象类型

当使用 new 来创建对象时,Java 将调用一个特殊的方法——构造函数(constructor)来初始化该对象的实例变量

对象方法在类定义中进行定义

类定义的语法:

  • 类名应该首字母大写以区别于原始类型和变量名
  • 通常,在一个文件中只定义一个类,文件名应该和类名一样
  • 任何应用程序都应该提供一个启动类(startup class),启动类中应该包含一个名为 main 的方法,该方法即为程序的执行入口。其它类也可以包含 main 方法,但是这个 main 方法不会被执行。

接下来以 Time 类作为示例,首先在类定义的开始处定义实例变量:

class Time {
	int hour, minute;
	double second;
}

构造函数

  • 构造函数的名字与类名相同
  • 构造函数没有返回类型和返回值,编译器通过有无返回类型的声明来识别构造函数
  • 关键字 static 被省略
public Time() {
	this.hour = 0;
	this.minute = 0;
	this.secont = 0.0;
}

public Time(int hour, int minute, double second) {
	this.hour = hour;
	this.minute = minute;
	this.secont = second;
}

ASSN 4 - Library

我们提供了 Book 和 Library 两个类,用于实现图书数据库的功能。你需要实现其中缺失的方法,以使这些类能够正常工作。

Book 类实现

首先,我们需要一个类来对书籍进行建模。请先创建一个名为 Book 的类,并复制粘贴下方的代码框架。该类定义了获取书名、查询书籍是否可借、借书以及还书的方法。不过,我们提供的框架中缺少这些方法的具体实现。请在方法体中填入相应的代码。main 方法用于测试这些方法。运行程序时,输出结果应如下所示:

Title (should be The Da Vinci Code): The Da Vinci Code
Rented? (should be false): false
Rented? (should be true): true
Rented? (should be false): false 
public class Book {

    String title;
    boolean borrowed;

    // Creates a new Book
    public Book(String bookTitle) {
        // Implement this method
        this.title = bookTitle;
        this.borrowed = false;
    }
   
    // Marks the book as rented
    public void rented() {
        // Implement this method
        this.borrowed = true;
    }
   
    // Marks the book as not rented
    public void returned() {
        // Implement this method
        this.borrowed = false;
    }
   
    // Returns true if the book is rented, false otherwise
    public boolean isBorrowed() {
        // Implement this method
        return this.borrowed;
    }
   
    // Returns the title of the book
    public String getTitle() {
        // Implement this method
        return this.title;
    }

    public static void main(String[] arguments) {
        // Small test of the Book class
        Book example = new Book("The Da Vinci Code");
        System.out.println("Title (should be The Da Vinci Code): " + example.getTitle());
        System.out.println("Borrowed? (should be false): " + example.isBorrowed());
        example.rented();
        System.out.println("Borrowed? (should be true): " + example.isBorrowed());
        example.returned();
        System.out.println("Borrowed? (should be false): " + example.isBorrowed());
    }
}

Library 类实现

接下来,我们需要创建一个类来表示各个图书馆,并管理其中的藏书。所有图书馆的开放时间相同:每天上午 9 点至下午 5 点。不过,它们的地址和藏书(即 Book 对象的数组)各不相同。
请创建一个名为 Library 的类,并复制粘贴下方的代码框架。我们提供了一个 main 方法,用于创建两个图书馆并对其中的书籍执行一些操作;但目前该类缺少所有必要的方法和成员变量。你需要定义并实现这些缺失的方法。请阅读 main 方法并查看编译错误,以确定具体缺少哪些方法。

有些方法需要定义为静态方法,而另一些则需要定义为实例方法。
比较 String 对象时请务必小心。应使用 string1.equals(string2) 来比较 string1string2 的内容。
建议采取分步实现的方式。首先注释掉整个 main 方法,然后逐行取消注释。运行程序,确保前几行代码能正常工作,接着取消下一行的注释并调试通过,依此类推。

import java.util.ArrayList;

public class Library {
    // Add the missing implementation to this class
    public String address;
    public ArrayList<Book> books;

    public Library(String address) {
        this.address = address;
        this.books = new ArrayList<Book>();
    }

    public void addBook(Book book) {
        this.books.add(book);
    }

    public static void printOpeningHours() {
        System.out.println("9 AM to 5 PM");
    }

    public void printAddress() {
        System.out.println(this.address);
    }

    public void borrowBook(String title){
        for (Book book : books) { // Java语法糖,等价于 for (int i = 0; i < books.size(); i++) { Book book = books.get(i); ... }
            if (book.getTitle().equals(title)) {
                if (!book.isBorrowed()) {
                    book.rented();
                    System.out.println("You successfully borrowed " + title);
                    return;
                } else {
                    System.out.println("Sorry, this book is already borrowed.");
                    return;
                }
            }
        }
        System.out.println("Sorry, this book is not in our catalog.");
    }

    public void printAvailableBooks() {
        boolean hasAvailableBooks = false;
        for (Book book : books) {
            if (!book.isBorrowed()) {
                System.out.println(book.getTitle());
                hasAvailableBooks = true;
            }
        }
        if (!hasAvailableBooks) {
            System.out.println("No book in catalog");
        }
    }

    public void returnBook(String title) {
        for (Book book : books) {
            if (book.getTitle().equals(title)) {
                if (book.isBorrowed()) {
                    book.returned();
                    System.out.println("You successfully returned " + title);
                    return;
                } else {
                    System.out.println("This book was not borrowed.");
                    return;
                }
            }
        }
        System.out.println("Sorry, this book is not in our catalog.");
    }

    public static void main(String[] args) {
        // Create two libraries
        Library firstLibrary = new Library("10 Main St.");
        Library secondLibrary = new Library("228 Liberty St.");

        // Add four books to the first library
        firstLibrary.addBook(new Book("The Da Vinci Code"));
        firstLibrary.addBook(new Book("Le Petit Prince"));
        firstLibrary.addBook(new Book("A Tale of Two Cities"));
        firstLibrary.addBook(new Book("The Lord of the Rings"));

        // Print opening hours and the addresses
        System.out.println("Library hours:");
        printOpeningHours();
        System.out.println();

        System.out.println("Library addresses:");
        firstLibrary.printAddress();
        secondLibrary.printAddress();
        System.out.println();

        // Try to borrow The Lords of the Rings from both libraries
        System.out.println("Borrowing The Lord of the Rings:");
        firstLibrary.borrowBook("The Lord of the Rings");
        firstLibrary.borrowBook("The Lord of the Rings");
        secondLibrary.borrowBook("The Lord of the Rings");
        System.out.println();

        // Print the titles of all available books from both libraries
        System.out.println("Books available in the first library:");
        firstLibrary.printAvailableBooks();
        System.out.println();
        System.out.println("Books available in the second library:");
        secondLibrary.printAvailableBooks();
        System.out.println();

        // Return The Lords of the Rings to the first library
        System.out.println("Returning The Lord of the Rings:");
        firstLibrary.returnBook("The Lord of the Rings");
        System.out.println();

        // Print the titles of available from the first library
        System.out.println("Books available in the first library:");
        firstLibrary.printAvailableBooks();
    }
} 

包与访问控制

基本概念

应对协作开发中出现类名称相同的情况

package 包名称.子包名称

此后编译需要使用 -d 参数指定生成目录

javac -d ./out file.java

然后执行该类时需要输入完整的包.类名称

java com.example.hello

如果几个类存放在不同的包中,则在使用类的时候需要通过 import 导入

import 包名称.子包名称.类名称	// 手工导入类
import 包名称.子包名称.*	// JVM自动加载所需要的类

注意,一个类如果要被外部包访问,则此类一定要定义成 public class

静态导入:JDK 1.5 之后,如果一个类中的方法全是使用 static 声明的静态方法,则在导入的时候可以直接使用 import static 的方式导入,然后就可以直接输入方法名调用了静态方法

系统常见包

包名称 作用
java.lang 基本包,String 类就保存在此包中,JDK 1.0 后自动导入
java.lang.reflect 反射机制包,是 java.lang 的子包
java.util 工具包,一些常用的类库、日期操作等都在此包
java.text 文本处理类库
java.sql 数据库操作包
java.net 网络编程
java.io 输入、输出处理
java.awt 构成抽象窗口工具集,用来构建 GUI
javax.swing 建立图形用户界面

访问控制权限

  • private:私有访问权限,只能在本类中进行访问
  • default:默认可以被本包中的其它类访问,但不能被其它包的类所访问
  • protected:可被本包的类及不同包的子类(即此类需要继承该 protected 类)所访问
  • public:可被所有类访问,不管是否在同一个包中

所以权限上 public > protected > default > private


Java 类集框架

接口 描述
Collection 存放一组单值的最大接口
List Collection 接口的子接口,允许重复
Set Collection 接口的子类,不允许重复
Map 存放一对值的最大接口
Iterator 集合的输出接口,只能进行从前到后到单向输出
ListIterator Iterator 的子接口,可以进行双向输出
Enumeration 用于输出指定集合中的内容
SortedSet 单值的排序接口
SortedMap 存放一对值的排序接口
Queue 队列接口
Map.Entry 每个 Map.Entry 对象都保存着一对 key-value 内容,每个 Map 接口中都保存着多个 Map.Entry 接口实例

List

扩展方法:

方法 描述
public void add(int index, E element) 在指定位置增加元素
public boolean addAll(int index, Collection<?extend E> c) 在指定位置增加一组元素
E get(int index) 返回指定位置的元素
public int indexOf(Object o) 查找指定元素的位置
public int lastIndexOf(Object o) 从后向前查找指定元素的位置
public ListIterator<E> listIterator() 为 ListIterator 接口实例化
public E remove(int index) 按指定的位置删除元素
public List<E> subList(int fromIndex, int toIndex) 取出集合中的子集合
public E set(int index, E element) 替换指定位置的元素

常用子类如下

ArrayList

定义

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

demo:

import java.util.ArrayList;

public class ArrayListDemo {
    public static void main(String[] args) {
        // Create an ArrayList to store integers
        ArrayList<Integer> numbers = new ArrayList<>();

        // Add elements to the ArrayList
        numbers.add(10);
        numbers.add(20);
        numbers.add(30);
        numbers.add(40);
        numbers.add(50);

        // Display the elements in the ArrayList
        System.out.println("ArrayList elements: " + numbers);

        // Access an element at a specific index
        int elementAtIndex2 = numbers.get(2);
        System.out.println("Element at index 2: " + elementAtIndex2);
    }
}

将集合对象变为数组:

Object[] array = numbers.toArray();
System.out.println("Array elements: " + java.util.Arrays.toString(array));

Vector

public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

相较于 ArrayList,Vector 线程安全,可以使用 Iterator、foreach、Enumeration 输出(ArrayList 只能使用前两种)

LinkedList

链表操作类

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

Queue

队列操作接口,采用 FIFO(先进先出)

public interface Queue<E> extends Collection<E>
方法 描述
public E element() 找到链表的表头
public boolean off(E o) 将指定元素增加到链表的结尾
public E peek() 找到链表的头
public E poll() 找到并删除此链表的头
public E remove() 检索并移除表头

Set

不允许出现重复数据

public interface Set<E> extends Collection<E>

常用子类如下

HashSet

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable

demo:

import java.util.HashSet;
import java.util.Set;

public class HashSetDemo {
    public static void main(String[] args) {
        Set<String> allSet = new HashSet<>();
        allSet.add("A");
        allSet.add("B");
        allSet.add("C");
        allSet.add("C");
        allSet.add("C");
        allSet.add("D");
        System.out.println("HashSet elements: " + allSet);
    }
}

TreeSet

有序排列

public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable

Collection 的输出

  • Iterator
  • ListIterator
  • Enumeration
  • foreach

Iterator

public interface Iterator<E> {
    /**
     * Returns {@code true} if the iteration has more elements.
     * (In other words, returns {@code true} if {@link #next} would
     * return an element rather than throwing an exception.)
     *
     * @return {@code true} if the iteration has more elements
     */
    boolean hasNext();

    /**
     * Returns the next element in the iteration.
     *
     * @return the next element in the iteration
     * @throws NoSuchElementException if the iteration has no more elements
     */
    E next();

    /**
     * Removes from the underlying collection the last element returned
     * by this iterator (optional operation).  This method can be called
     * only once per call to {@link #next}.  The behavior of an iterator
     * is unspecified if the underlying collection is modified while the
     * iteration is in progress in any way other than by calling this
     * method.
     *
     * @implSpec
     * The default implementation throws an instance of
     * {@link UnsupportedOperationException} and performs no other action.
     *
     * @throws UnsupportedOperationException if the {@code remove}
     *         operation is not supported by this iterator
     *
     * @throws IllegalStateException if the {@code next} method has not
     *         yet been called, or the {@code remove} method has already
     *         been called after the last call to the {@code next}
     *         method
     */
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

    /**
     * Performs the given action for each remaining element until all elements
     * have been processed or the action throws an exception.  Actions are
     * performed in the order of iteration, if that order is specified.
     * Exceptions thrown by the action are relayed to the caller.
     *
     * @implSpec
     * <p>The default implementation behaves as if:
     * <pre>{@code
     *     while (hasNext())
     *         action.accept(next());
     * }</pre>
     *
     * @param action The action to be performed for each element
     * @throws NullPointerException if the specified action is null
     * @since 1.8
     */
    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}
方法 描述
public boolean hasNext() 判断是否有下一个值
public E next() 取出当前元素
public void remove() 移除当前元素

ListIterator

双向迭代操作

public interface ListIterator<E> extends Iterator<E> {
    // Query Operations

    /**
     * Returns {@code true} if this list iterator has more elements when
     * traversing the list in the forward direction. (In other words,
     * returns {@code true} if {@link #next} would return an element rather
     * than throwing an exception.)
     *
     * @return {@code true} if the list iterator has more elements when
     *         traversing the list in the forward direction
     */
    boolean hasNext();

    /**
     * Returns the next element in the list and advances the cursor position.
     * This method may be called repeatedly to iterate through the list,
     * or intermixed with calls to {@link #previous} to go back and forth.
     * (Note that alternating calls to {@code next} and {@code previous}
     * will return the same element repeatedly.)
     *
     * @return the next element in the list
     * @throws NoSuchElementException if the iteration has no next element
     */
    E next();

    /**
     * Returns {@code true} if this list iterator has more elements when
     * traversing the list in the reverse direction.  (In other words,
     * returns {@code true} if {@link #previous} would return an element
     * rather than throwing an exception.)
     *
     * @return {@code true} if the list iterator has more elements when
     *         traversing the list in the reverse direction
     */
    boolean hasPrevious();

    /**
     * Returns the previous element in the list and moves the cursor
     * position backwards.  This method may be called repeatedly to
     * iterate through the list backwards, or intermixed with calls to
     * {@link #next} to go back and forth.  (Note that alternating calls
     * to {@code next} and {@code previous} will return the same
     * element repeatedly.)
     *
     * @return the previous element in the list
     * @throws NoSuchElementException if the iteration has no previous
     *         element
     */
    E previous();

    /**
     * Returns the index of the element that would be returned by a
     * subsequent call to {@link #next}. (Returns list size if the list
     * iterator is at the end of the list.)
     *
     * @return the index of the element that would be returned by a
     *         subsequent call to {@code next}, or list size if the list
     *         iterator is at the end of the list
     */
    int nextIndex();

    /**
     * Returns the index of the element that would be returned by a
     * subsequent call to {@link #previous}. (Returns -1 if the list
     * iterator is at the beginning of the list.)
     *
     * @return the index of the element that would be returned by a
     *         subsequent call to {@code previous}, or -1 if the list
     *         iterator is at the beginning of the list
     */
    int previousIndex();


    // Modification Operations

    /**
     * Removes from the list the last element that was returned by {@link
     * #next} or {@link #previous} (optional operation).  This call can
     * only be made once per call to {@code next} or {@code previous}.
     * It can be made only if {@link #add} has not been
     * called after the last call to {@code next} or {@code previous}.
     *
     * @throws UnsupportedOperationException if the {@code remove}
     *         operation is not supported by this list iterator
     * @throws IllegalStateException if neither {@code next} nor
     *         {@code previous} have been called, or {@code remove} or
     *         {@code add} have been called after the last call to
     *         {@code next} or {@code previous}
     */
    void remove();

    /**
     * Replaces the last element returned by {@link #next} or
     * {@link #previous} with the specified element (optional operation).
     * This call can be made only if neither {@link #remove} nor {@link
     * #add} have been called after the last call to {@code next} or
     * {@code previous}.
     *
     * @param e the element with which to replace the last element returned by
     *          {@code next} or {@code previous}
     * @throws UnsupportedOperationException if the {@code set} operation
     *         is not supported by this list iterator
     * @throws ClassCastException if the class of the specified element
     *         prevents it from being added to this list
     * @throws IllegalArgumentException if some aspect of the specified
     *         element prevents it from being added to this list
     * @throws IllegalStateException if neither {@code next} nor
     *         {@code previous} have been called, or {@code remove} or
     *         {@code add} have been called after the last call to
     *         {@code next} or {@code previous}
     */
    void set(E e);

    /**
     * Inserts the specified element into the list (optional operation).
     * The element is inserted immediately before the element that
     * would be returned by {@link #next}, if any, and after the element
     * that would be returned by {@link #previous}, if any.  (If the
     * list contains no elements, the new element becomes the sole element
     * on the list.)  The new element is inserted before the implicit
     * cursor: a subsequent call to {@code next} would be unaffected, and a
     * subsequent call to {@code previous} would return the new element.
     * (This call increases by one the value that would be returned by a
     * call to {@code nextIndex} or {@code previousIndex}.)
     *
     * @param e the element to insert
     * @throws UnsupportedOperationException if the {@code add} method is
     *         not supported by this list iterator
     * @throws ClassCastException if the class of the specified element
     *         prevents it from being added to this list
     * @throws IllegalArgumentException if some aspect of this element
     *         prevents it from being added to this list
     */
    void add(E e);
}

foreach

for(类 对象 : 集合){
	// 集合操作
}

Map

Map 接口可以同时保存 key=value 的两个数据,这样就可以实现通过 key 查找相应 value 的操作

public interface Map<K,V>
方法或类 描述
public void clear() 清空 Map 集合
public boolean containsKey(Object key) 判断指定的 key 是否存在
public boolean containsValue(Object value) 判断指定的 value 是否存在
public Set<Map.Entry<K, V>> entrySet() 将 Map 对象变为 Set 集合
public boolean equals(Object o); 对象比较
public V get(Object key) 根据 key 取得 value
public int hashCode() 返回哈希码
public boolean isEmpty() 判断集合是否为空
public Set<K> keySet() 取得所有的 key
public V put(K key, V value) 向集合中加入元素
public void putAll(Map<? extends K, ? extends V> m) 将一个 Map 集合中的内容加入到另一个 Map
public V remove(Object key) 根据 key 删除 value
public int size() 取出集合的长度
public Collection<V> values() 取出全部的 value

Map.Entry

用于保存 key-value 的内容

public static interface Map.Entry<K,V>
方法或类 描述
public boolean equals(Object o) 对象比较
public K getKey() 取得 key
public V getValue() 取得 value
public int hashCode() 返回哈希码
public V setValue(V value) 设置 value 的值

Map 的常用子类如下

HashMap

无序存放,key 不允许重复

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable

demo:

import java.util.HashMap;
import java.util.Map;

public class MapDemo {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("key1", "value1");
        map.put("key2", "value2");
        map.put("key3", "value3");

        System.out.println("Map elements: " + map);
    }
}

Hashtable

旧的操作类,和 HashMap 的区别是前者的 key 和 value 不允许保存 null

TreeMap

可以按 key 排列

WeakHashMap

弱引用的 Map 集合,当集合中的某些内容不再使用时清除掉无用的数据,使用 gc 回收

IdentityHashMap

key 可以重复的 Map 集合


注意

不能直接使用迭代输出 Map 中的全部内容,因为 Map 存放的是键值对,而 Iterator 每次只能找到一个值


ASSN 5 - Graphics!

在我们提供的初始窗口中添加三种不同的形状。
向窗口中添加三个 BouncingBox 类的实例,让它们沿不同方向移动。请使用 ArrayList 来存储这些实例。

part 1

打开 DrawGraphics 类。其中的 draw 方法负责绘制窗口内容。目前,该方法绘制了一条线段和一个带边框的正方形;如果愿意,你可以将其移除。请在窗口中至少添加三种不同的形状。
查阅 java.awt.Graphics 类的 API 文档,了解其提供的方法。你可以绘制矩形、圆弧、线段、文本、椭圆、多边形,甚至(如果愿意多花点功夫)还可以绘制图像。发挥你的创意吧!

import java.awt.Color;
import java.awt.Graphics;

public class DrawGraphics {
    BouncingBox box;
    
    /** Initializes this class for drawing. */
    public DrawGraphics() {
        box = new BouncingBox(200, 50, Color.RED);
    }

    /** Draw the contents of the window on surface. Called 20 times per second. */
    public void draw(Graphics surface) {
        surface.drawLine(50, 50, 250, 250);
        surface.drawArc(61, 16, 61, 61, 61, 161);
        surface.drawOval(100, 20, 100, 150);
        surface.drawRect(100, 100, 100, 100);
        box.draw(surface);
    }
} 

part 2

DrawGraphics 类支持动画功能。draw 方法会以每秒 20 次的频率被调用,以绘制每一帧画面。BouncingBox 类同样具备动画支持。若要让方框移动,可在 DrawGraphics 的构造函数中调用 setMovementVector 方法,并传入 x 和 y 方向的位移量。例如,传入值 (1, 0) 会使方框缓慢向右移动,而 (0, -2) 则会使其较快地向上移动。只需调用一次该方法,方框便会持续沿该方向移动;换言之,请勿在 draw 方法中调用 setMovementVector,而应在构造函数中进行调用。
请在窗口中添加至少三个向不同方向移动的方框。为此,可在 DrawGraphics 的构造函数中创建一个 ArrayList,并将三个 BouncingBox 实例存入其中。随后,在 DrawGraphics.draw 方法内使用循环,依次调用每个方框的 draw 方法。

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;

public class DrawGraphics {
    BouncingBox box;
    BouncingBox box1;
    BouncingBox box2;
    BouncingBox box3;
    ArrayList<BouncingBox> boxes = new ArrayList<>();

    /** Initializes this class for drawing. */
    public DrawGraphics() {
        box = new BouncingBox(200, 50, Color.RED);
        box.setMovementVector(1, 0);
        box.setMovementVector(0, -2);

        box1 = new BouncingBox(100, 100, Color.BLUE);
        box1.setMovementVector(2, 1);
        box2 = new BouncingBox(150, 150, Color.GREEN);
        box2.setMovementVector(-1, 2);
        box3 = new BouncingBox(200, 200, Color.ORANGE);
        box3.setMovementVector(1, -1);
        boxes.add(box1);
        boxes.add(box2);
        boxes.add(box3);
    }

    /** Draw the contents of the window on surface. Called 20 times per second. */
    public void draw(Graphics surface) {
        surface.drawLine(50, 50, 250, 250);
        surface.drawArc(61, 16, 61, 61, 61, 161);
        surface.drawOval(100, 20, 100, 150);
        surface.drawRect(100, 100, 100, 100);
        box.draw(surface);

        for (BouncingBox box : boxes) {
            box.draw(surface);
        }
    }
} 

继承

class 父类{}
class 子类 extends 父类{}

注意:

  • 只允许多层继承不允许一次多重继承
  • 子类不能直接访问父类中的私有成员,需要通过其它操作(如 setter 或 getter)
  • 实例化子类对象会先调用父类的构造方法,再调用子类的构造方法

覆写

子类定义类与父类中同名的方法,但是子类覆写的方法不能拥有比父类方法更加严格的访问权限


super()

使用 super 可以从子类中调用父类中的构造方法、普通方法、属性,语句必须放在子类构造方法的首行


final 关键字

  • 使用 final 声明的类不能有子类
  • 使用 final 声明的方法不能被子类所覆写
  • 使用 final 声明的变量即称为常量,不可修改

抽象类

  • 包含一个抽象方法的类必须是抽象类
  • 抽象类和抽象方法都要使用 abstract 关键字声明
  • 抽象方法只需声明而不需要实现
  • 抽象类必须被子类继承,子类(如果不是抽象类)必须覆写抽象类中的全部抽象方法
abstract class 抽象类名称{
	属性 ;
	访问权限 返回值类型 方法名称(参数){	// 普通方法
		[return 返回值];
	}
	访问权限 abstract 返回值类型 方法名称(参数)	// 抽象方法,无方法体
}

注意抽象方法访问权限不可定义成 private,否则无法被子类继承


接口

interface 接口名称{
	全局常量 ;
	抽象方法 ;
}

注意接口中的抽象方法必须定义成 public 权限(在接口中无论是否出现 public 标识其方法权限均为 public)

实现接口:

class 子类 implements 接口A,接口B,...{
}

多态

Java 中面向对象主要有两种体现:

  • 方法的重载与覆写
  • 对象的多态性

对象的多态性主要分为以下两种类型:

  • 向上转型:子类对象 -> 父类对象
  • 向下转型:父类对象 -> 子类对象
父类 父类对象 = 子类实例;	// 向上转型
子类 子类对象 = (子类)父类实例;	// 向下转型

demo:

// 1. 父类(也可以是接口)
class Animal {
    public void makeSound() {
        System.out.println("动物发出未知的叫声");
    }
}

// 2. 子类继承父类并重写方法
class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("汪汪汪!");
    }
}

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("喵喵喵!");
    }
}

public class TestPolymorphism {
    public static void main(String[] args) {
        // 3. 父类引用指向子类对象(向上转型)
        Animal myDog = new Dog();
        Animal myCat = new Cat();
        
        // 表现出多态性:表面上调用的都是Animal的makeSound,但实际执行的是子类的逻辑
        myDog.makeSound(); // 输出: 汪汪汪!
        myCat.makeSound(); // 输出: 喵喵喵!
    }
    
    // 多态的巨大优势在于这里:
    // 这个方法可以接收任何Animal的子类对象,不需要为Dog和Cat单独写方法
    public static void letItSound(Animal animal) {
        animal.makeSound(); 
    }
}

instanceof 关键字

可以使用 instanceof 关键字判断一个对象到底是哪个类的实例