10-面向对象思考
-
10.1 引言
- 类的设计!
-
10.2 类的抽象和封装
- 类的抽象是指将类的实现和类的使用分离开,实现的细节被封装并且对用户隐藏,这被称为类的封装。类也称为抽象数据类型。
package chapter10; import java.util.Scanner; public class TestLoanClass { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.print("Enter annual interest rate, for example, 8.25: "); double annualInterestRate = input.nextDouble(); System.out.print("Enter number of years as an integer: "); int numberOfYears = input.nextInt(); System.out.print("Enter loan amount, for example 120000.95: "); double loanAmount = input.nextDouble(); Loan loan = new Loan(annualInterestRate,numberOfYears,loanAmount); System.out.printf("The loan was created on %s\n" + loan.getLoanDate().toString(),loan.getMonthlyPayment(),loan.getTotalPayment()); } }
package chapter10; public class Loan { private double annualInterestRate; private int numberOfYears; private double loanAmount; private java.util.Date loanDate; public Loan(){ this(2.5,1,1000); } public Loan(double annualInterestRate,int numberOfYears,double loanAmount){ this.annualInterestRate = annualInterestRate; this.loanAmount = loanAmount; this.numberOfYears = numberOfYears; loanDate = new java.util.Date(); } public double getAnnualInterestRate(){ return annualInterestRate; } public void setAnnualInterestRate(double annualInterestRate){ this.annualInterestRate = annualInterestRate; } public int getNumberOfYears(){ return numberOfYears; } public void setNumberOfYears(int numberOfYears){ this.numberOfYears = numberOfYears; } public double getLoanAmount(){ return loanAmount; } public void setLoanAmount(double loanAmount){ this.loanAmount = loanAmount; } public double getMonthlyPayment(){ double monthlyInterestRate = annualInterestRate / 1200; double monthlyPayment = loanAmount * monthlyInterestRate / (1 - (1 / Math.pow(1 + monthlyInterestRate,numberOfYears * 12))); return monthlyPayment; } public double getTotalPayment(){ double totalPayment = getMonthlyPayment() * numberOfYears * 12; return totalPayment; } public java.util.Date getLoanDate(){ return loanDate; } }
- 类的抽象是指将类的实现和类的使用分离开,实现的细节被封装并且对用户隐藏,这被称为类的封装。类也称为抽象数据类型。
-
10.3 面向对象的思想
- 假设需要将体重和身高同一个人的名字与出生日期相关联,虽然可以分别声明几个变量来存储这些值,但是这些值不是紧密耦合在一起的。将他们耦合在一起的理想方法是创建一个将他们全部包含的对象。因为这些值都被绑定在单独的对象上,所以他们应该存储在实例数据域中。
package chapter10; public class UseBMIClass { public static void main(String[] args) { BMI bmi1 = new BMI("Kim Yang",18,145,70); System.out.println("The BMI for " + bmi1.getName() + " is " + bmi1.getBMI() + " " + bmi1.getStatus()); BMI bmi2 = new BMI("Susan King",215,70); System.out.println("The BMI for " + bmi2.getName() + " is " + bmi2.getBMI() + " " + bmi2.getStatus()); } }
package chapter10; public class BMI { private String name; private int age; private double weight; private double height; public static final double KILOGRAMS_PER_POUND = 0.45359237; public static final double METERS_PER_INCH = 0.0254; public BMI(String name,int age,double weight,double height){ this.name = name; this.weight = weight; this.height = height; this.age = age; } public BMI(String name,double weight,double height){ this(name,20,weight,height); } public double getBMI(){ double bmi = weight * KILOGRAMS_PER_POUND / ((height * METERS_PER_INCH) * (height * METERS_PER_INCH)); return Math.round(bmi * 100) / 100.0; } public String getStatus(){ double bmi = getBMI(); if (bmi < 18.5) return "Underweight"; else if (bmi < 25) return "Normal"; else if (bmi < 30) return "Overweight"; else return "Obese"; } public String getName(){ return name; } public int getAge(){ return age; } public double getWeight(){ return weight; } public double getHeight() { return height; } public BMI(String name,int age,double weight,double feet,double inches){ this.name = name; this.age = age; this.weight = weight; this.height = feet * 12 + inches; } }
- 假设需要将体重和身高同一个人的名字与出生日期相关联,虽然可以分别声明几个变量来存储这些值,但是这些值不是紧密耦合在一起的。将他们耦合在一起的理想方法是创建一个将他们全部包含的对象。因为这些值都被绑定在单独的对象上,所以他们应该存储在实例数据域中。
-
10.4 类的关系
为了设计类,需要套就类之间的关系。类之间的关系通常有关联、聚合、组合以及继承。-
10.4.1 关联
- 关联由两个类之间的实线表示,可以有一个可选的标签描述关系。标签是Take和Teach。每个关系可以有一个可选的黑色三角形表明关系的方向。
[图片上传失败...(image-a3f01c-1604228938568)]
- 关联由两个类之间的实线表示,可以有一个可选的标签描述关系。标签是Take和Teach。每个关系可以有一个可选的黑色三角形表明关系的方向。
-
10.4.2 聚集和组合
- 聚集是关联的一种特殊形式,代表了两个对象之间的归属关系。聚集对has-a关系进行建模。所有者对象称为聚集对象,他的类称为聚集类。而从属对象称为被聚集对象,他的类称为被聚集类。
- 如果被聚集对象的存在依赖于聚集对象,我们称这两个帝乡之间的关系是组合。换句话说,被聚集对象不能单独存在。一个对象拥有另外一个对象,当拥有者对象销毁了,依赖对象也会销毁。
- 由于聚集和组合关系都以同样的方式用类来表示,为了简单起见,我们不区分他们,将两者称为组合。
-
-
10.5 示例学习:设计Course类
package chapter10; public class TestCourse { public static void main(String[] args) { Course course1 = new Course("Data Structures"); Course course2 = new Course("Database Systems"); course1.addStudent("Peter Jones"); course1.addStudent("Kim Smith"); course1.addStudent("Anne Kennedy"); course2.addStudent("Peter Smith"); course2.addStudent("Steve Smith"); System.out.println("Number of students in course1; " + course1.getNumberOfStudents()); String[] students = course1.getStudents(); for (int i = 0;i < course1.getNumberOfStudents();i++) System.out.print(students + ", "); System.out.println(); System.out.print("Number of students in course2: " + course2.getNumberOfStudents()); } }
package chapter10; public class Course { private String courseName; private String[] students = new String[100]; private int numberOfStudents; public Course(String courseName){ this.courseName = courseName; } public void addStudent(String student){ students[numberOfStudents] = student; numberOfStudents++; } public String[] getStudents(){ return students; } public int getNumberOfStudents(){ return numberOfStudents; } public String getCourseName(){ return courseName; } public void dropStudent(String student){ } }
-
10.6 示例学习:设计栈类
package chapter10; public class TestStackOfIntegers { public static void main(String[] args) { StackOfIntegers stack = new StackOfIntegers(); for (int i = 0;i < 10;i++) stack.push(i); while (!stack.empty()) System.out.print(stack.pop() + " "); } }
package chapter10; public class StackOfIntegers { private int[] elements; private int size; private static final int DEFAULT_CAPACITY = 16; public StackOfIntegers(){ this(DEFAULT_CAPACITY); } public StackOfIntegers(int capacity){ elements = new int[capacity]; } public void push(int value){ if (size >= elements.length){ int[] temp = new int[elements.length * 2]; System.arraycopy(elements,0,temp,0,elements.length); elements = temp; } elements[size++] = value; } public int pop(){ return elements[--size]; } public int peek(){ return elements[size - 1]; } public boolean empty(){ return size == 0; } public int getSize(){ return size; } }
-
10.7 将基本数据类型值作为对象处理
- 出于对性能的考虑,在Java中基本数据类型不作为对象使用,因为处理对象需要额外的系统开销。
- Java提供了一个方便的办法,即将基本数据类型合并为或者说包装为对象(将int包装为Integer类,将double包装为Double类,将char包装为Character类)通过使用包装类,可以将基本数据类型值作为对象处理。
- 大多数基本类型的包装类的名称和对应的剧本数据类型名称一样,第一个字母要大写,按时int和char就不是对应的,需要注意一下。
- 那么,现在就可以既使用基本数据类型又使用表示数值的字符串来构造包装类。
new Double(5.0) new DOuble("5.0")
- 包装类没有无参构造方法。所有包装类的实例都是不可变的,这意味着一旦创建对象后,他们的内部值就是不能再改变的。
- ,每一个数值包装类都有常量MAX_VALUE和MIN_VALUE。MAX_VALUE表示对应的基本数据类型的最大值。对于Byte、Short、Integer和Long而言,MIN_VALUE表示对应的基本数据类型byte、short、int和long的最小值。对于Float和Double类而言,MIN_VALUE表示float型和double型的最小正值。
- 每个包装类都包含各自方法doubleValue()、floatValue()......这些方法返回包装对象对应的double、float....值.
new Double(12.4).intValue()//returns 12; new Integer(12).doubleValue()//returns 12.0;
- compareTo方法用于比较两个数值,并且如果该数值大于、等于或小于另外一个数值时,分别返回1、0、-1。
new Double(12.4).compareTo(new Double(12.3))//returns 1; new Double(12.3).compareTo(new Double(12.3))//returns 0; new Double(12.3).compareTo(new Double(12.51))//returns -1;
- 数值包装类有一个有用的静态方法valueOf(String s),该方法创建一个新对象,并将它初始化为指定字符串表示的值。
Double doubleObject = Double.valueOf("12.4") Integer integerObject = Integer.valueOf("12")
- Integer类中的parseInt方法讲一个数值字符串转换为一个int值,也使用过Double类中的parseDouble方法讲一个数值字符串转换为一个double值。每个数值包装类都有两个重载的方法,将数值字符串转换为正确的以10(十进制)或指定值为基数(2表示二进制,8表示八进制)的数值。
- 可以使用format方法将一个十进制数转换为十六进制数。
String.format("%x",26)//returns 1A
-
10.8 基本类型和包装类类型之间的自动转换
- 将基本类型值转换为包装类对象的过程称为装箱,相反的叫做拆箱。Java允许基本类型和保障类型之间进行自动转换。如果一个基本类型值出现在需要的环境中,编译器会将基本类型值进行自动装箱,如果一个对象出现在需要基本类型值的环境中,编译器会将对象进行自动拆箱。这成为自动装箱和自动拆箱。
-
10.9 BigInteger和BigDecimal类
- BigInteger类和BigDecimal类可以用于表示任意大小和精度的整数或者十进制数。
- 可以使用add、subtract、multiple、divide和remainder方法进行算术运算。使用compareTo方法比较两个大数字。
- BigDecimal对象可以达到任意精度。如果不能终止运行,那么divide方法会抛出ArithmetricException异常。但是可以使用重载的divide(BigDecimal d,int scale,int roundingMode)方法来指定scale值和舍入方式来避免这个异常,这里的scale是指小数点后最小的整数位数。
package chapter10; import java.math.BigInteger; import java.util.Scanner; public class LargeFactorial { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.print("Enter an integer: "); int n = input.nextInt(); System.out.println(n + "! is \n" + factorial(n)); } public static BigInteger factorial(long n){ BigInteger result = BigInteger.ONE; for (int i = 1;i <= n;i++) result = result.multiply(new BigInteger(i + "")); return result; } }
-
10.10 String类
String对象是不可变的,字符串一旦创建,内容是不可以改变的。- 10.10.1 构造字符串
- 可以使用字符串字面值或字符数组创建一个字符串对象。
String newString = new String(stringLiteral); String message = new String("Welcome to Java");
- Java将字符串字面值看做String对象所以下面的语法合法的:
String message = "Welcome to Java";
- 还可以用字符数组创建一个字符串:
char[] charArray = {'G','o','o','d',' ','D','a','y'}; String message = new String(charArray);
- 可以使用字符串字面值或字符数组创建一个字符串对象。
- 10.10.2 不可变字符串与驻留字符串
- String对象是不可变的。
String s = "Java"; s = "HTML";
- 第一条语句创建了一个内容为“Java” 的String对象,并将其应用赋值给s。第二条语句创建了一个内容为“HTML”的心String对象,并将其引用赋值给s。赋值后第一个String对象仍然存在,但是不能访问他了,因为变量s现在指向了新的对象。
- 因为字符串会频繁的使用到,Java虚拟机为了提高效率并节约内存,对机油形同字符串序列的字符串字面值使用同一个实例。这样的实例称为驻留的字符串。
结果显示:String s1 = "Welcome to Java"; String s2 = new String("Welcome to Java"); String s3 = "Welcome to Java"; System.out.println("s1 == s2 is " + (s1 == s2)); System.out.println("s1 == s3 is " + (s1 == s3));
- String对象是不可变的。
- 10.10.1 构造字符串
```java
s1 == s2 is false
s1 == s3 is true
```
由于s1和s3指向相同的驻留字符串,所以s1==s3。
- 10.10.3 替换和拆分字符串
- 方法replace、replaceFirst和replaceAllhi返回一个源自原始字符串的心字符串(并未改变原始字符串)
- 10.10.4 使用模式匹配、替换和拆分
- 这里使用的就是正则表达式!!!
- 10.10.5 字符串与数组之间的转换
- 为了将字符串转换为一个字符数组,可以使用toCharArray方法。
java char[] chars = "Java".toCharArray();
- 还可以使用方法getChars(int srcBegin,int srcEnd,cgar[] dst,int dstBegin)将下标从srcBegin到srcEnd-1的子串赋值到字符数组dst中下标从dstBegin开始的位置。
java char[] dst = {'J','a','v','a','1','3','0','1'}; "CS3720".getChars(2,6,dst,4);
- 为了将一个字符数组转换成一个字符串,应该使用构造方法String(char[])或者方法valueOf(char[])
java String str = new String(new char[]{'J','a','v','a'}); String str = String.valueOf(new char[]{'J','a','v','a'});
- 10.10.6 字符串和数值转换成字符串
- 10.10.7 格式化字符串
- String类包含静态方法format,他可以创建一个格式化的字符串。调用该方法的语法是:
```java
String.format(format,item1,item2,...,itemk);
```
- 这个方法和printf方法类似,知识format方法返回一个格式化的字符串,而printf方法显示一个格式化的字符串。
```java
Stirng s = String.format("%7,2f%6d%-4s",45.556,14,"AB);
```
-
10.11 StringBuilder类和StringBuffer类
StringBuilder类和StringBuffer类类似于String类,区别在于String类是不可改变的。
StringBuffer类中修改缓冲区的方法是同步的,这意味着只有一个任务被允许执行该方法,除此之外,StringBuffer和StringBuilder类是很相似的。欧式多任务并发访问,就是用StringBuffer,因为这种情况下需要同步以防止StringBuffer损坏。而如果是单人舞访问,使用StringBuilder会更加有效。
-
10.11.1 修改StringBuilder中的字符串
- 除了setCharAt方法之外,所有这些进行修改的方法否做两件事:
- 改变字符串构建器的内容;
- 返回字符串构建器的引用。
-
如果一个字符串不需要任何改变,则会用String而不要使用StringBuilder。String比StringBuilder更加高效。
- 除了setCharAt方法之外,所有这些进行修改的方法否做两件事:
-
10.11.2 toString、capacity、length、setLength和charAt方法
- capacity()方法返回字符串构建器当前的容量。容量是指在不增加构建器大小的亲哪个框喜爱能够存储的字符数量。
- length()方法返回字符串构建器中实际存储的字符数量。setLength(newLength)方法设置字符串构建器的长度。如果参数newLength小于字符串构建器的当前长度,则字符串构架器会被截短到敲好能包含由参数newLength给定的字符个数。如果参数newLength大于或等于当前长度,则给字符串构建器追加足够多的空字符(‘\u0000’),使其长度length编程新参数newLength。参数newLength必须大于等于0。
-
charAt(index)方法返回字符串构建器中指定下标index位置的字符,下标是基于0的,参数index必须大于等于0,并且小于字符串构建器长度。
-
10.11.3 示例学习:判断回文串时忽略既非字母又非数字的字符
package chapter10; import java.util.Scanner; public class PalindromeIgnoreNonAlphanumeric { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.print("Enter a string : "); String s = input.nextLine(); System.out.println("Ingnoring nonalphanumric characters, \nis " + s + " a palindrome? " + isPalingdrome(s)); } public static boolean isPalingdrome(String s){ String s1 = filter(s); String s2 = filter(s1); return s2.equals(s1); } public static String filter(String s){ StringBuilder stringBuilder = new StringBuilder(); for (int i = 0;i < s.length();i++) if (Character.isLetterOrDigit(s.charAt(i))) stringBuilder.append(s.charAt(i)); return stringBuilder.toString(); } public static String reverse(String s){ StringBuilder stringBuilder = new StringBuilder(s); stringBuilder.reverse(); return stringBuilder.toString(); } }
-