Java学习笔记(抽象类和接口)
- 学习说明
- 抽象类和接口
- 抽象类
-
- 举例讲解
- 接口
- Comparable举例讲解
- ActionListener接口
- Cloneable接口
- 接口与抽象类
- 将基本数据类型作为对象处理
- 对一个对象数组排序
- 大数值计算BigInteger、BigDecimal类
- Rational类
学习说明
要点:
了解抽象类的简单使用
了解为什么要使用抽象类
了解抽象类使用的注意点
了解接口的基本使用
熟悉组件监听事件的添加
了解 clone 方法
了解接口与抽象类的区别
了解对对象数组进行排序的使用(这里还存在着问题,代码运行不正确)
熟悉大数类(BigInteger、BigDecimal)的使用
学习资料为:《Java语言程序设计》梁勇
抽象类和接口
抽象类
抽象方法:不能实现
抽象类里面只要有抽象方法就好了,不需要每个方法都是抽象方法
UML里面抽象方法用斜体表示(抽象类、属性也是斜体)
public abstract class GeometricObject {
protected String color;
protected boolean filled;
protected Date dateCreated;
public GeometricObject() {
// TODO Auto-generated constructor stub
dateCreated = new Date();
}
public GeometricObject(String color, boolean filled) {
// TODO Auto-generated constructor stub
dateCreated = new Date();
this.color = color;
this.filled = filled;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public boolean isFilled() {
return filled;
}
public void setFilled(boolean filled) {
this.filled = filled;
}
public Date getDateCreated() {
return dateCreated;
}
public String toString() {
return "created on " + dateCreated + "ncolor" + color + "and filled" + filled;
}
public abstract double getArea(); // 有抽象方法的类就是抽象类
public abstract double getPerimeter();
}
为什么要用抽象类
为了更简单的设计,看下面
// 继承GeometricObject的第一个类Circle
public class Circle extends GeometricObject {
private double radius;
public Circle() {
// TODO Auto-generated constructor stub
setRadius(1.0);
}
public Circle(double radius) {
// TODO Auto-generated constructor stub
this.radius = radius;
}
public Circle(double radius, String color, boolean filled) {
// TODO Auto-generated constructor stub
super(color, filled);
this.radius =radius;
}
@Override // 继承抽象类则必须要实现抽象方法,除非继承的类也是抽象类
public double getArea() {
// TODO Auto-generated method stub
return radius * radius * Math.PI;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
@Override
public double getPerimeter() {
// TODO Auto-generated method stub
return 2 * radius * Math.PI;
}
}
// 继承GeometricObject的第二个类Rectangle
public class Rectangle extends GeometricObject {
private double width;
private double height;
public Rectangle() {
// TODO Auto-generated constructor stub
width = 2.0;
height = 1.0;
}
public Rectangle(double width, double height) {
// TODO Auto-generated constructor stub
this.width = width;
this.height = height;
}
public Rectangle(double width, double height, String color, boolean filled) {
// TODO Auto-generated constructor stub
super(color, filled);
this.width = width;
this.height = height;
}
@Override // 实现了抽象方法
public double getArea() {
// TODO Auto-generated method stub
return width * height;
}
@Override
public double getPerimeter() {
// TODO Auto-generated method stub
return 2 * width + 2* height;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
}
// 抽象类和上面一样
// 测试主函数
public static void main(String[] args) {
GeometricObject object1 = new Circle(5);
GeometricObject object2 = new Rectangle(4, 3);
System.out.println("The two objects have the same area? " + equalArea(object1, object2));
displayGeometricObject(object1);
displayGeometricObject(object2);
}
// 这里进行函数书写的时候,参数会少很多,且类型更统一,方便理解
public static boolean equalArea(GeometricObject object1, GeometricObject object2) {
return object1.getArea() == object2.getArea();
}
public static void displayGeometricObject(GeometricObject object) {
System.out.println();
System.out.println("The area is " + object.getArea());
System.out.println("The perimeter is " + object.getPerimeter());
}
抽象类的注意点
抽象类可以不包含抽象方法,但是如果有抽象方法,则该类必须为抽象类(后面讲到的接口也可以)
父类不是抽象类,但子类是可以为抽象类的(这样的用法不多)
对于方法也是如此,父类中不是抽象的方法,在子类中也可以改为抽象的
举例讲解
以日历类 Calender 和 GregorianCalender
Calender类的域常量
YEAR
MONTH
DATE
HOUR
HOUR_OF_DAY
MINUTE
SECOND
DAY_OF_WEEK
DAY_OF_MONTH
DAY_OF_YEAR
WEEK_OF_MONTH
WEEK_OF_YEAR
AM_PM(0表示上午,1表示下午)
// 直接通过 Calender.属性 获得的值并不一定正确,都很奇怪,需要采用get进行获取
public static void main(String[] args) {
System.out.println(Calendar.YEAR);
System.out.println(Calendar.MONTH);
System.out.println(Calendar.DATE);
System.out.println(Calendar.HOUR);
System.out.println(Calendar.HOUR_OF_DAY);
System.out.println(Calendar.MINUTE);
System.out.println(Calendar.SECOND);
System.out.println(Calendar.DAY_OF_WEEK);
System.out.println(Calendar.DAY_OF_MONTH);
System.out.println(Calendar.DAY_OF_YEAR);
System.out.println(Calendar.WEEK_OF_MONTH);
System.out.println(Calendar.WEEK_OF_YEAR);
System.out.println(Calendar.AM_PM);
}
// 正确的获取方式
public static void main(String[] args) {
Calendar calendar = new GregorianCalendar(); // 采用公历计数
System.out.println(calendar.get(Calendar.YEAR));
System.out.println(calendar.get(Calendar.MONTH));
System.out.println(calendar.get(Calendar.DATE));
System.out.println(calendar.get(Calendar.HOUR));
System.out.println(calendar.get(Calendar.HOUR_OF_DAY));
System.out.println(calendar.get(Calendar.MINUTE));
System.out.println(calendar.get(Calendar.SECOND));
System.out.println();
System.out.println(calendar.get(Calendar.DAY_OF_WEEK));
System.out.println(calendar.get(Calendar.DAY_OF_MONTH));
System.out.println(calendar.get(Calendar.DAY_OF_YEAR));
System.out.println(calendar.get(Calendar.WEEK_OF_MONTH));
System.out.println(calendar.get(Calendar.WEEK_OF_YEAR));
System.out.println(calendar.get(Calendar.AM_PM));
}
// 注意月份,月份从0开始,为0-11
接口
接口里面只包含常量和抽象方法
接口和抽象类很像,但也有一些区别,例如可多实现接口,但只能继承一个类
实现接口同样需要实现全部方法(应该是抽象方法的,但接口里面的方法都是抽象的),但如果是抽象类实现接口的话,则不要求
// 定义一个接口
public interface Edible {
public abstract String howToEat();
}
class Animal{
}
// Chicken既继承类又实现接口(这个书写顺序不能乱,要先继承,再实现接口)
class Chicken extends Animal implements Edible{
@Override
public String howToEat() {
// TODO Auto-generated method stub
return "Chicken fry it";
}
}
// 用于对照的
class Tiger extends Animal{
}
// 抽象类实现接口,可以不用实现方法
abstract class Fruit implements Edible{
}
// 但下面的具体类就需要实现了
class Apple extends Fruit{
@Override
public String howToEat() {
// TODO Auto-generated method stub
return "Apple make apple pie";
}
}
class Orange extends Fruit{
@Override
public String howToEat() {
// TODO Auto-generated method stub
return "Orange make orange juice";
}
}
// 测试主函数
public static void main(String[] args) {
Object[] objects = {new Tiger(), new Chicken(), new Apple()};
for (int i = 0; i < objects.length; i++) {
if(objects[i] instanceof Edible)
System.out.println(((Edible)objects[i]).howToEat()); // 要十分注意这个类型转换的括号,"."运算符的优先级最高,所以需要把前面的转化部分都括起来
else {
System.out.println("is not instanceof Edible");
}
}
}
接口的常量的理解
书写方面,其实省略书写是没问题的,编译器自动补全了
属性可以通过 接口名.属性来访问(因为是静态的)
// 下面两种写法是一致的
public interface Edible {
public static int NUM = 4;
public abstract String howToEat();
}
public interface Edible {
int NUM = 4;
String howToEat();
}
// 测试主函数
public static void main(String[] args) {
System.out.println(Edible.NUM);
}
Comparable举例讲解
// 接口
public interface Comparable {
int compareTo(Object object);
}
// Max类的写法,这里直接传入参数Comparable对象,是为了有更好的鲁棒性
// 简单来说,如果不是这个类型,则编译就会报错,如果在后面进行类型转化后再比较就只能在运行时才会报错了
public class Max {
public static Comparable max(Comparable comparable1, Comparable comparable2) {
if(comparable1.compareTo(comparable2) > 0)
return comparable1;
return comparable2;
}
}
// 一个矩形类,实现接口,进行比较
public class ComparableRectangle implements Comparable {
private double width;
private double height;
public ComparableRectangle(double width, double height) {
// TODO Auto-generated constructor stub
this.width = width;
this.height = height;
}
@Override
public int compareTo(Object object) {
// TODO Auto-generated method stub
if(this.getArea() > ((ComparableRectangle)object).getArea())
return 1;
else if(this.getArea() < ((ComparableRectangle)object).getArea())
return -1;
return 0;
}
public double getArea() {
return width * height;
}
}
// 测试主函数
public static void main(String[] args) {
ComparableRectangle rectangle1 = new ComparableRectangle(3, 4);
ComparableRectangle rectangle2 = new ComparableRectangle(2, 6);
ComparableRectangle rectangle3 = new ComparableRectangle(3, 5);
System.out.println(rectangle1.compareTo(rectangle2));
System.out.println(rectangle1.compareTo(rectangle3));
System.out.println(rectangle3.compareTo(rectangle2));
}
ActionListener接口
按钮触发事件,监听器的使用
public class HanldleEvent extends JFrame {
public static void main(String[] args) {
JFrame frame = new HanldleEvent();
frame.setTitle("Handle Event");
frame.setSize(300, 200);
frame.setLocation(200, 100);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public HanldleEvent() {
// TODO Auto-generated constructor stub
JButton jbtnOK = new JButton("OK");
JButton jbtnCancel = new JButton("Cancel");
JPanel panel = new JPanel();
panel.add(jbtnOK);
panel.add(jbtnCancel);
add(panel);
OKListenerClass listener1 = new OKListenerClass();
CancelListenerClass listener2 = new CancelListenerClass();
jbtnOK.addActionListener(listener1);
jbtnCancel.addActionListener(listener2);
}
}
// 创建监听器类,实现了执行方法函数
class OKListenerClass implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
System.out.println("OK button clicked");
}
}
class CancelListenerClass implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
System.out.println("Cancel button clicked");
}
}
Cloneable接口
下面演示原有的clone方法
public static void main(String[] args) {
Calendar calendar = new GregorianCalendar(2022, 5, 21);
Calendar calendar1 = calendar;
Calendar calendar2 = (Calendar)calendar.clone(); // 这里复制的是内容,创建了新的对象
System.out.println("calender == calender1 is " + (calendar == calendar1));
System.out.println("calender == calender2 is " + (calendar == calendar2));
System.out.println("calender equals calender1 is " + calendar.equals(calendar1));
System.out.println("calender equals calender2 is " + calendar.equals(calendar2));
}
接下来自己写一个覆盖掉原有的clone方法
// 创建一个House类
public class House implements Cloneable, Comparable {
private int id;
private double area;
private Date buildDate;
public House(int id, double area) {
// TODO Auto-generated constructor stub
this.id = id;
this.area = area;
buildDate = new Date();
}
public int getId() {
return id;
}
public double getArea() {
return area;
}
public Date getBuildDate() {
return buildDate;
}
// 这里实现了父类的方法,但实现的是浅复制,即只是复制每个内在对象的引用,但又生成了一个新的对象
// 这个方法如果不重写,则会在后面运行的时候报错
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public int compareTo(Object object) {
// TODO Auto-generated method stub
if(area > ((House)object).area)
return 1;
else if(area < ((House)object).area)
return -1;
return 0;
}
public void printInfo() {
System.out.println(id);
System.out.println(area);
System.out.println(buildDate);
}
}
// 测试主函数
public static void main(String[] args) throws CloneNotSupportedException {
House house1 = new House(1, 100);
House house2 = (House) house1.clone();
house1.printInfo();
house2.printInfo();
// 不管是 == 还是 equals 返回都是false
System.out.println("house1 == house2 is " + (house1 == house2));
System.out.println("house1.equals(house2) is " + house1.equals(house2));
// 但每个内在对象(属性),返回的都是true
System.out.println("house1.getArea() == house2.getArea() is " + (house1.getArea() == house2.getArea()));
System.out.println("house1.getBuildDate() == house2.getBuildDate() is " + (house1.getBuildDate() == house2.getBuildDate()));
System.out.println("house1.getId() == house2.getId() is " + (house1.getId() == house2.getId()));
}
接口与抽象类
抽象类的修饰符没有限制
但接口的修饰符
变量必须为public static final修饰
方法必须为public abstract修饰
类只能单继承,但接口可以多扩展,类可以实现多个接口,接口之间也可以多个扩展
// 类的继承与实现接口
public class NewClass extends BaseClass implements Interface1, Interface2 …… InterfaceN{
}
//
public interface NewInterface extends Interface1, Inetrface2 …… InterfaceN{
}
使用场景:
强关系的(父子关系)用类的继承
弱关系的(类属关系,即对象拥有某种属性)用接口
还是很抽象啊,一般接口可以为形容词,类则为名词,描述具有属性的,则用接口(Comparable)
接口可以定义不相关类的共有父类型,比抽象类更加灵活
这里就很抽象了,应该和上面那个使用场景结合在一起理解
关系弱,很多类具有相同的属性,例如可比较
(其实不用太纠结,以后写着写着慢慢修改就好)
将基本数据类型作为对象处理
出于对性能的考虑,Java并没有把基本数据类型作为对象使用
但在一些场景需要考虑通用性,就可以这么干了(也正是因为基本数据类型都有包装类)
对一个对象数组排序
类实现Comparable接口
这里的问题很奇怪,没有进行compareTo的重写,就很迷惑
public class GenericSort {
public static void main(String[] args) {
Integer[] intArr = {new Integer(4), new Integer(3), new Integer(2), new Integer(5) };
Double[] doubleArr = {new Double(1.2), new Double(2.3), new Double(12), new Double(10)};
Character[] charArr = {new Character('A'), new Character('D'), new Character('C'), new Character('B')};
String[] stringArr = { "Hello", "hello", "abcd" };
sort(intArr);
sort(doubleArr);
sort(charArr);
sort(stringArr);
printList(intArr);
printList(doubleArr);
printList(charArr);
printList(stringArr);
}
public static void sort(Comparable[] list) {
Comparable currentMin;
int currentMinIndex;
// 采用选择排序进行排序
for (int i = 0; i < list.length - 1; i++) {
currentMin = list[i];
currentMinIndex = i;
for (int j = i + 1; j < list.length; j++) {
if (currentMin.compareTo(list[j]) > 0) {
currentMin = list[i];
currentMinIndex = j;
}
}
if (currentMinIndex != i) {
list[currentMinIndex] = list[i];
list[i] = currentMin;
}
}
}
public static void printList(Object[] list) {
for (int i = 0; i < list.length; i++) {
System.out.print(list[i] + " ");
}
System.out.println();
}
}
大数值计算BigInteger、BigDecimal类
基本使用如下:
// 这里不能采用普通的加减乘除,需要调用函数进行计算
public static void main(String[] args) {
BigInteger bigInteger = new BigInteger("1234567890");
BigInteger bigInteger2 = new BigInteger("3");
System.out.println(bigInteger.multiply(bigInteger2));
BigDecimal bigDecimal = new BigDecimal(1.0);
BigDecimal bigDecimal2 = new BigDecimal(3);
// 这个表示取10位小数,最后一个进位
System.out.println(bigDecimal.divide(bigDecimal2, 10, BigDecimal.ROUND_UP));
}
阶乘计算如下:
public class Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Please input a num : ");
int num = scanner.nextInt();
System.out.printf("%e", getFactorial(num));
}
public static double getFactorial(int num) {
BigInteger result = BigInteger.ONE;
for (int i = 1; i <= num; i++) {
result = result.multiply(new BigInteger(i + ""));
}
return result.doubleValue(); // 转化为浮点数数值类型,但同时也就不能够计算过大的数字了
}
}
Rational类
有理数类(自己创建的一个类,复习一下前面的知识)
// 蛮长,蛮细节的一个类
public class Rational {
private long numerator = 0;
private long denominator = 1;
public Rational() {
// TODO Auto-generated constructor stub
this(0, 1);
}
public Rational(long numerator, long denominator) {
// TODO Auto-generated constructor stub
long gcd = gcd(numerator, denominator);
this.numerator = ((denominator > 0) ? 1 : -1) * numerator / gcd;
this.denominator = Math.abs(denominator) / gcd;
}
public long getNumerator() {
return numerator;
}
public long getDenominator() {
return denominator;
}
public Rational add(Rational secondRational) {
long n = numerator * secondRational.getDenominator() + denominator * secondRational.getNumerator();
long d = denominator * secondRational.getDenominator();
return new Rational(n, d);
}
public Rational subtract(Rational secondRational) {
long n = numerator * secondRational.getDenominator() - denominator * secondRational.getNumerator();
long d = denominator * secondRational.getDenominator();
return new Rational(n, d);
}
public Rational multiply(Rational secondRational) {
long n = numerator * secondRational.getNumerator();
long d = denominator * secondRational.getDenominator();
return new Rational(n, d);
}
public Rational divide(Rational secondRational) {
long n = numerator * secondRational.getDenominator();
long d = denominator * secondRational.getNumerator();
return new Rational(n, d);
}
public String toString() {
if (denominator == 1)
return numerator + "";
else {
return numerator + "/" + denominator;
}
}
public long gcd(long n, long d) {
long n1 = Math.abs(n);
long n2 = Math.abs(d);
long gcd = 1;
for (int i = 1; i <= n1 && i <= n2; i++) {
if (n1 % i == 0 && n2 % i == 0)
gcd = i;
}
return gcd;
}
public boolean equals(Object parm) {
if((this.subtract((Rational)parm)).getNumerator() == 0)
return true;
return false;
}
public int intValue() {
return (int)doubleValue();
}
public float floatValue() {
return (float)doubleValue();
}
public double doubleValue() {
return numerator * 1.0 / denominator;
}
public long longValue() {
return (long)doubleValue();
}
// 这里需要注意括号的位置,实际这个类并不难理解,只是初中,甚至小学的数学知识
public int compareTo(Object object) {
if(this.subtract((Rational)object).getNumerator() > 0)
return 1;
else if(this.subtract((Rational)object).getNumerator() < 0)
return -1;
return 0;
}
}
// 测试主函数
// 结果符合一般的分数运算规则,注意精度,这里的精度达不到太高,且long类型长度有限制
public static void main(String[] args) {
Rational rational1 = new Rational(3, 5);
Rational rational2 = new Rational(1, 2);
System.out.println(rational1.add(rational2));
System.out.println(rational1.subtract(rational2));
System.out.println(rational1.multiply(rational2));
System.out.println(rational1.divide(rational2));
System.out.println(rational1.intValue());
System.out.println(rational1.floatValue());
System.out.println(rational1.doubleValue());
System.out.println(rational1.longValue());
}