Java类继承:
Java中的类继承就是定义一个通用的类,然后把它扩展为更为特殊的类,这些特殊的类继承通用类中的特征和方法。
在Java当中如果C1扩展自类C2,那么称为C2称为父类、超类或基类,C1称为子类,子类从父类中继承可访问的数据域和方法,还可以添加新的数据域和新的方法
比如现在有一个类GeometricObject从它扩展为两个类一个Circle类一个是Rectangle类
从一个父类扩展出一个子类的语法是:publicclass Circle extends GeometricObject
表示创建一个类Circle并从GeometricObject类扩展
这里使用到关键字extends
我们看到父类中有如下的定义:
private String color = "white";
privatebooleanfilled; // 这个的默认值为false
private Date dateCreated;
那么的话在子类中则不可以直接去使用这些数据域只能通过父类的set、get方法来写入或读取数据
所以在子类中的构造方法中可以看到:
public Circle(double radius, String color, boolean filled) {
/*
* 这是一种方法 this.radius = radius;
* setColor(color);//这里是调用父类的方法
* super.setFilled(filled);//还可以加上super关键字
*/
// 另一种写法:
super(color, filled);// 明确调用父类的构造方法,这种方法调用父类的构造方法要写在第一行
this.radius = radius;
}
import java.text.SimpleDateFormat;
import java.util.Date;
//父类
publicclass GeometricObject {
private String color = "white";
privatebooleanfilled; // 这个的默认值为false
private Date dateCreated;
// 构造方法
public GeometricObject() {
}
public GeometricObject(String color, boolean filled) {
dateCreated = new Date();
this.color = color;
this.filled = filled;
}
// get、set方法
public String getColor() {
returncolor;
}
publicvoid setColor(String color) {
this.color = color;
}
publicboolean isFilled() {
returnfilled;
}
publicvoid setFilled(boolean filled) {
this.filled = filled;
}
public Date getDateCreated() {
returndateCreated;
}
// toString 方法用来返回对象的字符串描述
public String toString() {
return"创建于"
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(dateCreated) + "\n颜色是:" + color + "\n填充情况:"
+ filled;
}
}
publicclass Circle extends GeometricObject {
// Circle类中特有的属性
privatedoubleradius;
// 构造方法
public Circle() {
}
public Circle(double radius) {
this.radius = radius;
}
public Circle(double radius, String color, boolean filled) {
/*
* 这是一种方法 this.radius = radius;
* setColor(color);//这里是调用父类的方法
* super.setFilled(filled);//还可以加上super关键字
*/
// 另一种写法:
super(color, filled);// 明确调用父类的构造方法,这种方法调用父类的构造方法要写在第一行
this.radius = radius;
}
// get、set方法
publicdouble getRadius() {
returnradius;
}
publicvoid setRadius(double radius) {
this.radius = radius;
}
// 普通方法
// 获得圆的面积
publicdouble getArea() {
returnradius * radius * Math.PI;
}
// 获得直径
publicdouble getzj() {
return 2 * radius;
}
// 获得周长
publicdouble getzc() {
return 2 * Math.PI * radius;
}
// 输出这个圆的对象信息
public String toString() {
return"圆的半径:" + radius + "\n圆的面积:" + getArea() + "\n圆的直径:" + getzj()
+ "\n圆的周长:" + getzc() + "\n圆的颜色:" + super.getColor()
+ "\n是否填充:" + super.isFilled();
}
}
publicclass Rectangle extends GeometricObject {
privatedoublewidth;
privatedoubleheight;
// 构造方法
public Rectangle() {
}
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
public Rectangle(double width, double height, String color, boolean filled) {
super(color, filled);
this.width = width;
this.height = height;
}
// get、set方法
publicdouble getWidth() {
returnwidth;
}
publicvoid setWidth(double width) {
this.width = width;
}
publicdouble getHeight() {
returnheight;
}
publicvoid setHeight(double height) {
this.height = height;
}
// 普通方法
publicdouble getArea() {
returnwidth * height;
}
publicdouble getzc() {
return 2 * (width + height);
}
// toString方法
public String toString() {
return"矩形的宽:" + width + "\n矩形的长:" + height + "\n矩形的面积:" + getArea()
+ "\n矩形的周长:" + getzc() + "\n颜色:" + super.getColor() + "\n是否填充:"
+ super.isFilled();
}
}
上面的例子就是关于继承方面的一些说明
1, 子类并不是父类的一个子集
2, 父类中如果有私有数据在其外是不可以访问的,就算是在它的继承类中也是不可以访问的,只能通过父类中的公共的get、set方法进行访问修改
3, 如果要从B扩展出A, 那么A应该要比B包含更多的信息
4, 一个父类与一个子类之间必须要存在一种“是”的关系
5, Java中是不可以出现多重继承的,Java中的多重继承只能通过接口来实现
关于super关键字:
它的作用就是为了在子类中去调用父类的构造方法和父类的方法
super()表示调用父类的无参构造方法
super(参数)表示调用父类的有参构造方法
在子类的构造方法中调用父类的构造方法时这个调用语句必须要写在第一行,父类的构造方法是不被子类继承的只能使用super关键字来调用
构造方法链:
构造方法可以调用重载的构造方法或是它的父类的构造方法如果它们都没有被显示的调用则会自动把super()作为构造方法的第一条语句
在任何情况下,构造一个类的时例的时候,都会调用沿着继承链的所有父类构造方法,也就是在创建一个子类对象的时候,子类的构造方法在完成自己的构造任务前会先调用父类的构造方法,这个过完程会一直沿着到最后一个构造方法被调用为止
通过上面我们可以看到如果在一类在调计的时候没有写上一个无参的构造方法的时候则在继承的时候在子类中要明确的使用super(参数)来调用父类的构造方法这样的话一是麻烦二是可以忘记调用父类的构造方法而导致错误出现,因而在设计一个类的时候一定要加上一个无参的构造方法
super也可以调用父类的方法
super.父类方法名(参数);
当然子类会继承父类的方法在一般的情况下是不用写上super关键字编译器也可以确认是调用的哪个函数,但是当子类中有与父类同名的方法这个时候就形成了一种方法的覆盖因而在子类中直接用方法名则表示调用本类中的这个方法这个时候要调用父类的这个方法时则必须加上super关键字
关于覆盖:
仅当实例方法是可访问的时候才可以被覆盖,私有方法在类本身之外不能被访问因而是不可以覆盖的,静态方法可以被继承但是注意它是不可以被覆盖的,父类中的静态方法在子类中被重新定义则只是说父类中的静态方法被隐藏而并没有覆盖还是可以通过父类名去调用父类中的这个静态方法
覆盖与重载:
重载一般是指有相同的方法名不同的方法签名的方法之间形成的一种重载,而要覆盖一个方法则要使用相同的签名并在子类中有相同的返回值类型
Java中的根类Object和toString方法:
java.lang.Object是Java的一个根类,所有的类都是从这个类继承而来的,当声明一个类没有明确指定父类的时候就默认认为是从Object继承而来的。
toString()方法
public String toString()
这个方法会返回一个对对象描述的字符串,默认返回的是“所属类名@这个对象十六进制内存地址”,这样的信息是没有太多作用的因而通常会对这个方法进行覆盖使其返回一个对类的描述性字符串
注意:System.out.println(object)与System.out.println(object.toString())是等价的
面向对象的特点中除了封装、继承还有一个就是多态。
关于一个类它其实就是一个类型,对于父类定义的类型就是父类型,子类定义的类型就是子类型
继承关系使一个子类继承父类的特征并且附加一些新的特性,子类是父类的特殊化,一个子类的实例都是其父类的实例,但反过来一个父类的实例都是子类的实例是不正确的。因而可以把一个子类的实例传给父类型这是没有问题的。这就是多态一个父类可以有多种形态
动态绑定:
关于一个对象有两个类型,一个是声明类型,一个是实际类型
比如:Object o = new Circle();
Systme.out.println(o.toString());
那么这个时候如果Circle类中覆盖了Object类中的toString()方法,这个时候o的声明类型就是Object,而实际它指向的对象是一个Circle对象,那这个时候o.toString()则调用的还是它实际的类型中的toString()也就是Circle类中覆盖了Object类的toString方法,这一个过程就是动态绑定。
其实在一个类调用一个方法的时候JVM会从它的实际类型开始一直找它的父类及父的父类去找这个方法直到找到第一个为止就调用这个方法
对象的转换和instanceof运算符:
一个对象如果从子类到父类的转换是合法的这种是隐式转换比如
Object o = new Circle();
那么这个时候如果使用Circle c = o;这样就是不行的JVM认为一个父类不可以直接转为一个子类对象,这时必须要使用显示转换
Circle c = (Circle)o;
总是可以进行向上转换把一个子类转为一个父类这不会有问题
但要把一个父类转为一个子类必须要显示的进行JVM才会通过编译
这个时候虽然是可以显示的把父类对象转为子类对象但是注意这时要保证父类对象是一个子类的对象如果不是在运行时还是会出现异常的(ClassCastException)
把在进行向下转换的时候应该是都要进行判断使用instanceof进行判断
myObject instanceof Circle 这个会返回一个boolean值
表示测试myObject对象是不是一个Circle对象
注意对于成员访问运算符来说它的优先级是高于转换运算符的要特别注意((Circle)object).getArea()这个写法
Object类中的equals方法:
public boolean equals(Object o)
这个方法是测试两个对象是否相等
object1.equals(object2);
在Object 类中它的默认实现是
public boolean equals(Object obj){
return (this==ob);
}
这里==运算符检测两个引用变量是否指向同一个对象,然而一般来说需要的是判断两个对象是否有相同的内容
所以这个方法也常常在客户类中进行覆盖
public boolean equals(Object o){
if(o!=null && o instanceof Circle){
reutrn radius==((Circle)o).radius;
}else
return false;
}
ArrayList类:
这是一个数组线性表,与数组相似,但是数组在一旦定义后其大小就固定了而ArrayList可以存储不限个数的内容
ArrayList()构造方法
add(Object o);在其尾部追加一个对象
clear();删除线性表中的所有元素
contains(Object o);如果这个线性表中包含元素o则返回true
get(int index);返回下标为index的元素
indexOf(Object o);返回线性表中第一个匹配元素o 的位置
isEmpty();判断这个线性表是否为空
lastIndexOf(Object o);线性表中最后一个匹配元素o的位置
remove(Object o);删除线性表中的元素o,成功删除则返回true
size();返回线性表中元素的个数
remove(int index);删除指定下标处的元素,成功会返回true
set(int index,Object o);设定特定下标处的元素为o
import java.util.ArrayList;
publicclass TestArrayList {
publicstaticvoid main(String[] args) {
ArrayList array = new ArrayList();
// 向这个ArrayList当中加处一些元素
array.add("谢声");
array.add("xiaoxie");
array.add("advent");
array.add("xies");
array.add("advent");
// 显示相应的结果
System.out.println("线性表当前元素个数:" + array.size());
System.out.println("xies这个元素在线性表中吗?" + array.contains("xies"));
System.out.println("在线性表中第一次出现advent的位置?" + array.indexOf("advent"));
System.out.println("这个线性表是空的吗?" + array.isEmpty());
// 在线性表的第二个位置加入一个新元素-----
array.add(2, "-----");
// 删除一个元素
array.remove("advent"); //它只会删除第一个匹配到的这个元素
array.remove(1);
System.out.println(array.toString());
// 反序显示线性表中的各元素
for (int i = array.size() - 1; i >= 0; i--) {
System.out.print(array.get(i) + " ");
}
System.out.println();
// 在线性表中存放自己定义的类的对象
ArrayList list = new ArrayList();
list.add(new Circle(2));
list.add(new Circle(4));
System.out.println("第一个元素的面积调用对象中的方法:"
+ ((Circle) list.get(0)).getArea());
}
}
可以使用ArrayList来实现一个栈类
import java.util.ArrayList;
publicclass MyStack {
private ArrayList list = new ArrayList();
publicboolean isEmpty() {
returnlist.isEmpty();
}
publicint getSize() {
returnlist.size();
}
public Object peek() {
returnlist.get(getSize() - 1);
}
public Object pop() {
returnlist.remove(getSize() - 1);
}
publicvoid push(Object o) {
list.add(o);
}
publicint search(Object o) {
returnlist.indexOf(o);
}
public String toString() {
return"stack:" + list.toString();
}
}
protected访问修饰符:
前面说过private:类外不可访问
public :类名可以访问
protected:表示在父类中的这个属性或方法在子类中可以访问,非子类不可以访问
private 修饰符可以完全隐藏类的成员,这样可以保证不可以从类外访问它们
不使用修饰符的话表中同一个包内可以直接访问不同的包中不可以访问
protected:允许在同一个包内或是子类中对其访问不同包中不是子类无法访问
public:则表示任意类进行访问
类的使用有两种:
一种是创建它的实例直接使用
一种是为了产生子类来扩展它
注意:private和protected只能用于类的成员,public和默认的无修饰符的可以用于类的成员和类
对于子类来说它可以覆盖父类中的protected方法,并把它的可访问性改为public,但子类是不可以削弱父类中定义的方法的可访问性的,比如在父类中一个方法的可访问性是public,那么这个方法在子类中覆盖也必须定义为public
有时也可能希望不再使类被扩展,比如String、Math、StringBuilder、StringBuffer等类就是终极类不可以再扩展的类,如果不希望扩展只需要在类声明的时候加上final就可以了
public final class C{
}
上面的这个类就是一个终极类了不可再扩展的
也可定义一个方法是终极的,这个时候这个方法就不可以被子类覆盖
public class Test{
public fina void m(){
}
}
上面的m方法在子类中是不可以覆盖的。
final还可以用在局部变量上,方法内的终极局部变量就是常量