文章目录
pre2-面向对象先导
基础知识
java的“对象”
在现实中,拿一条狗来举例,它的状态有:名字、品种、颜色,行为有:叫、摇尾巴和跑。
对比现实对象和软件对象,它们之间十分相似。软件对象也有状态和行为。软件对象的状态就是属性,行为通过方法体现。在软件开发中,方法操作对象内部状态的改变,对象的相互调用也是通过方法来完成。
java的“类”
类可以看成是创建 Java 对象的模板。例如,public class dog
是一个类,dog puppy
就是这个类下的一个对象。
只包含属性的类和C的结构体相似。
构造一个类
java类使用变量定义数据域,其中数据域应当定义为private
类型进行封装。
public class Bookset {
//类下定义的变量(类似结构体中的变量)
private String name;
public double price;
public int num;
//构造函数(一般命名和类的名称相同)
public Bookset(String name,double price,int num)
{
this.name = name;
this.price = price;
this.num = num;
}
}
类的方法
java类使用方法定义行为。
//访问属性
public String getName() {
return name;
}
//修改属性
public void setName(String name) {
this.name = name;
}
输入输出
import java.util.Scanner
Scanner in = new Scanner(System.in);
int a = in.nextInt();
double b = in.nextDouble();
String nextline = in.nextLine();
System.out.println("Hello world!");
System.out.print("a+b+nextline");
容器
java中不推荐使用静态数组,一般用容器作为替代,常见容器如ArrayList,HashMap,HashSet,LinkedList
等,此处先介绍ArrayList
。
ArrayList
ArrayList 是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
初始化
import java.util.ArrayList; // 引入 ArrayList 类
ArrayList<E> objectName =new ArrayList<>(); // 初始化
-
E: 泛型数据类型,用于设置 objectName 的数据类型,只能为引用数据类型(即为基本类型的包装类)。
-
-
此外,BigInteger、BigDecimal 用于高精度的运算,BigInteger 支持任意精度的整数,也是引用类型,但它们没有相对应的基本类型。
-
objectName: 对象名。
常见方法
方法 | 描述 |
---|---|
add() | 将元素插入到指定位置的 arraylist 中 |
addAll() | 添加集合中的所有元素到 arraylist 中 |
clear() | 删除 arraylist 中的所有元素 |
clone() | 复制一份 arraylist |
contains() | 判断元素是否在 arraylist |
get() | 通过索引值获取 arraylist 中的元素 |
indexOf() | 返回 arraylist 中元素的索引值 |
removeAll() | 删除存在于指定集合中的 arraylist 里的所有元素 |
remove() | 删除 arraylist 里的单个元素 |
size() | 返回 arraylist 里元素数量 |
isEmpty() | 判断 arraylist 是否为空 |
subList() | 截取部分 arraylist 的元素 |
set() | 替换 arraylist 中指定索引的元素 |
sort() | 对 arraylist 元素进行排序 |
toArray() | 将 arraylist 转换为数组 |
toString() | 将 arraylist 转换为字符串 |
ensureCapacity() | 设置指定容量大小的 arraylist |
lastIndexOf() | 返回指定元素在 arraylist 中最后一次出现的位置 |
retainAll() | 保留 arraylist 中在指定集合中也存在的那些元素 |
containsAll() | 查看 arraylist 是否包含指定集合中的所有元素 |
trimToSize() | 将 arraylist 中的容量调整为数组中的元素个数 |
removeRange() | 删除 arraylist 中指定索引之间存在的元素 |
replaceAll() | 将给定的操作内容替换掉数组中每一个元素 |
removeIf() | 删除所有满足特定条件的 arraylist 元素 |
forEach() | 遍历 arraylist 中每一个元素并执行特定操作 |
继承
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
extends
extends 只能继承一个类。
class 父类 {
}
class 子类 extends 父类 {
}
implements
使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。
public interface A {
public void eat();
public void sleep();
}
public interface B {
public void show();
}
public class C implements A,B {
}
方法
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
class Animal {
void eat() {
System.out.println("animal : eat");
}
}
class Dog extends Animal {
void eat() {
System.out.println("dog : eat");
}
void eatTest() {
this.eat(); // this 调用自己的方法
super.eat(); // super 调用父类方法
}
}
异常
捕获异常
try
{
// 程序代码
}catch(ExceptionName e1)
{
//Catch 块
}
Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。
实例:
import java.io.*;
public class ExcepTest{
public static void main(String args[]){
try{
int a[] = new int[2];
System.out.println("Access element three :" + a[3]);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("Exception thrown :" + e);
}
System.out.println("Out of the block");
}
}
可以在catch块后再添加catch块形成多重捕获。
抛出异常
如果一个方法没有捕获到一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。
实例:
import java.io.*;
public class className
{
public void withdraw(double amount) throws RemoteException, InsufficientFundsException
{
// Method implementation
}
//Remainder of class definition
}
一个方法抛出多个异常↑。
finally关键字
finally 关键字用来创建在 try 代码块后面执行的代码块。无论是否发生异常,finally 代码块中的代码总会被执行。
实例:
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}finally{
// 程序代码
}
自定义异常类型
//自定义异常类,继承Exception类
public class InsufficientFundsException extends Exception
{
//此处的amount用来储存当出现异常(取出钱多于余额时)所缺乏的钱
private double amount;
public InsufficientFundsException(double amount)
{
this.amount = amount;
}
public double getAmount()
{
return amount;
}
}
public class CheckingAccount
{
//balance为余额,number为卡号
private double balance;
private int number;
//方法:取钱
public void withdraw(double amount) throws InsufficientFundsException
{
if(amount <= balance)
{
balance -= amount;
}
else
{
double needs = amount - balance;
throw new InsufficientFundsException(needs);
}
}
}
public class BankDemo
{
public static void main(String [] args)
{
CheckingAccount c = new CheckingAccount(101);
System.out.println("Depositing $500...");
c.deposit(500.00);
try
{
System.out.println("\nWithdrawing $100...");
c.withdraw(100.00);
System.out.println("\nWithdrawing $600...");
c.withdraw(600.00);
}catch(InsufficientFundsException e)
{
System.out.println("Sorry, but you are short $" + e.getAmount());
//打印异常栈
e.printStackTrace();
}
}
}
task5似乎没有啥新的知识点。。。
字符串比较
str.equals(str2)
作业简介
task1
构造基本的书架类和访问、修改方法即可,注意浮点数在double精度范围内,整数在long的范围内。
task2
每个Bookset作为一个元素,构造ArrayList类作为书架。
public class Bookcase {
private ArrayList<Bookset> booksetArrayList = new ArrayList<>();
}
注意:
//容器对应指针,需要初始化
for (int i = 0; i < n; i++) {
Bookcase bookset = new Bookcase();
bookcaselist[i] = bookset;
}
对于每个操作,分别构造一个方法,例如查询丛书总价:
//查询书架丛书的总价
public BigDecimal getTotal() {
int i;
BigDecimal totalprice = new BigDecimal(0.0);
for (i = 0; i < booksetArrayList.size(); i++) {
totalprice = totalprice.add(booksetArrayList.get(i).getTotal());
}
return totalprice;
}
坑点:
- 数据求和时可能超出精度范围,因此需要使用BigInterger等类型,尤其需要注意该类型的转换方法。
- (经讨论区dl提出)即使书架类的丛书总价求和方法采用了
BigDecimal
来求和,但如果单种丛书的totalPrice
溢出也会导致程序错误。
task3
数据读取:
因为每行数据都可能不相同,因此建议直接读取一行数据,再用split
函数进行分割,每次需要使用的数据直接用values[n]
即可。
Scanner in = new Scanner(System.in);
String inline = in.nextLine();
String[] values = inline.split(" ");
继承关系:
依次建立几种图书类后,其他方法都可以从父类继承,只需重新定义构造函数和对新增属性的访问、修改方法可,以math
类为例:
public class Math extends OtherS {
//声明数据域
private Math(String type, String name, double price, long num, long year) {
super(type, name, price, num, year);
}
private long grade;
//构造函数
public Math(String type, String name, double price, long num, long year, long grade) {
super(type, name, price, num, year);
this.grade = grade;
}
//访问方法
public long getGrade() {
return grade;
}
}
工厂模式:
工厂模式主要解决接口选择的问题。
即定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
在本题中,即创建新的丛书时,根据题目需要进行创建,具体实现如下:
case ("OtherA"): {
long age = Long.parseLong(values[6].trim());
Bookset book = new OtherA(type, name, price, num, age);
bookcaselist[serial].AddBook(book);
break;
}
注意:
可以用trim()函数清除字符串前后的空格。
task4
根据题目要求定义4种异常类型,在main
或者Bookcase
类里面使用try catch
语句进行捕获即可。
坑点:
- 对于第一种操作而言,捕获异常的类型应该时先判断是不是空书架,再判断是否有名为name的书。
task5
新增加了 一个合并操作,也只需要针对这个操作添加相应的方法即可。
此处由于需要判断是否为同种书,我采用了较笨的逐个遍历的方法,先将case1的书全部复制到newcase,再将newcase的书和case2逐个比较判断。
此处判断用了一点技巧:先看书名,如果书名相同再看类型;类型都相同则比较价格,最后判断其他特有属性。这样可以减少比较次数。
private static boolean Compare(Bookset book, Bookset book2) {
if (book.getType().equals(book2.getType())) {
if ((book.getPrice() - book2.getPrice()) > 1e-5
|| (book2.getPrice() - book.getPrice()) > 1e-5) {
return false; } else {
switch (book.getType()) {
//以novel为例
case "Novel": {
Novel son = (Novel) book;
Novel son2 = (Novel) book2;
if ((son.getAge() == son2.getAge())
&& (son.getFinish() == son2.getFinish())) {
return true; } else {
return false; } }
default:
throw new IllegalStateException("Unexpected value: " + book.getType()); }
}
} else {
return false;
}
}
坑点:
-
复制书架不能直接使用bookcase.clone()!这样复制过去的是指针而非书中的元素,因此我新写了一个Clone方法,利用原书架中的丛书的值生成新对象,再逐个复制。
-
public void CloneBook(Bookset bookorigin) { String type = bookorigin.getType(); String name = bookorigin.getName(); double price = bookorigin.getPrice(); long num = bookorigin.getNum(); switch (type) { //以Other为例 case ("Other"): { Bookset book = new Bookset(type, name, price, num); booksetArrayList.add(book); break; } } }