(三)面向对象高级知识
3.1继承性
3.1.1继承的实现
class A extends (class) B
3.1.2继承的限制
1、java不允许多重继承 即 class A extends B,C
java允许多层继承 即class B extends A , class C extends B
*注意:*继承的层数一般不大于三层
2、子类在继承父类时,严格来讲会继承父类中的全部操作,但是对于所有的私有操作属于隐式继承,所有的非私有操作属于显示继承。
什么意思呢?就是讲B继承A,在B类内不能直接使用A的私有属性,要使用,也必须通过get方法获取
3、在子类对象构造前一定会默认调用父类的构造(默认使用无参构造),以保证父类的对象优先实例化,子类对象后实例化。
super()主要是调用父类的构造方法,放在子类构造方法的首行。如果父类中提供有无参构造方法时,加与不加super()无所谓。但是如果父类中没有无参构造方法,则必须明确地使用super()调用父类指定参数的构造方法。
class A{
private String str="helo";
public A(String aa){
System.out.println("aa:"+aa);
}
}
class B extends A{
public B(String aa){
super(aa);
System.out.println("nihao");
}
}
public class TestDemo12 {
public static void main(String args[]){
B b=new B("wow");
}
}
注意:在编写的过程中,父类提供的是有参构造,子类也得有一个有参构造
在编译过程中:There is no default constructor available in 'A
这个错误的意思就是 继承时你在子类中没有super()父类的有参构造函数
若一个父类只要有一个带参数的构造方法,那么在写其子类的构造方法时必须先通过super调用父类的构造方法才能完成子类的构造方法而且super只能写在子类构造方法体内的第一行。
3.2覆写
定义:在子类定义属性或方法时,有可能出现定义的属性或者方法与父类同名的情况,这样的操作叫做覆写。
1、在子类定义了覆写方法后,实例化子类对象之后调用的是覆写过的方法,要是想调用父类的方法 在此方法内第一行写 super.方法名()
程序运行之前,我们首先观察实例化的类是哪个
之后观察被调用的方法是否被覆写,如果覆写了,则调用覆写过的方法,否则调用父类方法。
2、子类方法的访问权限不能比父类方法的访问权限严格
3、父类方法使用private申明后不可以覆写。
class A{
public void fun(){
this.print();
}
private void print(){
System.out.println("nihao");
}
}
class B extends A{
public void print(){ //
//super.print(); //super.方法名 调用父类方法 这里调用print时编译出错
super.fun();
System.out.println("nihaoa");
}
}
public class TestDemo1 {
public static void main(String args[]){
B b=new B();
b.print();//虽然输出的像是覆写了一样,但其实这里调用的是B中定义的方法
//父类的private方法无法覆写
}
}
3.3final关键字
final成为终结器,可以定义类、方法和属性
1、使用final定义的类不能在有任何子类。任何类都不能继承以final声明的父类。
String是使用final定义的类,无法被继承
2、使用final定义的方法不能被子类所覆写
3、使用final定义的变量就成了常量,常量在定义时设置好内容就不再改变了。常量一般大写。
3.4多态性
3.4.1多态性体现在两方面:
(1)方法多态性
方法的重载:同一个方法名称,不同的参数类型及个数
方法的覆写:同一个方法,根据实例化对象不同,完成的功能也不同
(2)对象多态性
对象的向上转型: 父类名 父类对象=new 子类名()
对象的向下转型:子类名 子类对象名=(子类名)父类实例
3.4.2对象的向上向下转型
对象的向上转型
class T{
public void print(){
System.out.println("nihao");
}
}
class L extends T{
public void print(){ //
//super.print(); //super.方法名 调用父类方法 这里调用print时编译出错
//super.fun();
System.out.println("nihaoa");
}
}
public class test {
public static void main(String args[]){
/*
向上转型,自动转换
*/
T t=new L();
t.print();
/*
向下转型,强制转换
*/
T t2=new L(); //T是父类
L l=(L)t2;
l.print();
/*
向下转型操作是有前提的,必须发生向上转型后才可以发生向下转型
错误的向下转型
*/
T t3=new T();
L l2=(L)t3;
l2.print(); //会报classCastException异常
}
}
向上转型,最终调用的方法,不要看声明类名称,要看实例化对象的类。这里实例化了L类,将子类转换为父类对象,但是还是执行的是子类中覆写的方法。
向上转型:其目的是参数的统一,但是在向上转型中,通过子类实例化后的父类对象对象只能调用父类中定义过的方法。
向下转型:其目的是父类对象要调用实例化它的的子类中的特殊方法,但是向下转型容易带来安全隐患(两个没有关系的实例化对象不能进行向下转型,即没有发生过向上转型,就无法向下转型)
//用对象的向上转型实现参数的统一
class father{
public void print(){
System.out.println("hello,father!");
}
}
class child1 extends father{
public void print(){
System.out.println("hello,child1!");
}
}
class child2 extends father{
public void print(){
System.out.println("hello,child2!");
}
}
public class TestDemo4 {
public static void main(String args[]) {
fun(new child1());
fun(new child2());
}
public static void fun(father f){
f.print();
}
}
3.5抽象类
3.5.1抽象类的定义
1、有抽象方法的类必须被定义为抽象类(抽象类中可能不含有抽象方法)
abstract class abclass{
public void fun(){
System.out.println("hello,abclass!");
}
public abstract void print();//有抽象方法的类必须被定义为抽象类(抽象类中可能不含有抽象方法)
}
2、抽象类不能被实例化
3、如果要使用抽象类则
抽象类必须拥有子类。抽象类的子类只能继承一个抽象类
抽象类的子类必须覆写抽象类中的全部抽象方法(强制子类覆写)
依靠对象向上转型概念,可以通过抽象类的子类完成抽象类的实例化对象操作。
//定义一个抽象类
abstract class abclass{
public void fun(){
System.out.println("hello,abclass!");
}
public abstract void print();//有抽象方法的类必须被定义为抽象类(抽象类中可能不含有抽象方法)
}
//使用抽象类 定义子类
class abclasschild extends abclass{
public void fun(){
System.out.println("hello,abclass!");
}
public void print(){
super.fun();//使用父类的方法
System.out.println("hello,abclass!,use abstract function");
}
}
public class TestDemo5 {
public static void main(String args[]) {
abclass a=new abclasschild();
a.print();
}
}
3.5.2抽象类的相关限制
1、抽象类中会存在一些属性,那么在抽象类中肯定会存在构造方法,目的是属性初始化,并且子类对象实例化时,先执行父类构造,在调用子类构造。
//子类对象实例化时,先执行父类构造,在调用子类构造。
abstract class A1{
public A1(){
this.print();
}
public abstract void print();
}
class B1 extends A1{
private int num=100;
public B1(int num){
this.num=num;
}
public void print(){
System.out.println(num);
}
}
public class TestDemo6 {
public static void main(String args[]) {
B1 b1=new B1(30);
b1.print();
}
}
在这个程序中,最终输出0和30。
在实例化b1时,会先调用父类的构造,父类的构造调用子类覆写的方法,由于此时还没有调用子类的构造,所以num值为默认值0.当执行完子类的构造之后,才给num赋值为30.所以最终输出0,30
这里有一个问题 当初始化num时,将其定义为static 结果大不相同。这里希望之后的自己可以解答。
2、抽象类不能被final定义,因为抽象类必须拥有子类,而final定义的类不能拥有子类
3、抽象类中可以没有任何抽象方法,但是只要是抽象类,就不能使用关键字new进行实例化
4、抽象类中依然可以定义内部的抽象类,而实现的子类也可以根据需要选择是否定义内部类来继承抽象内部类
5、外部抽象类不能被static所修饰,而内部类可以加static。加了static内部类就相当于是一个外部抽象类,继承的时候使用“外部类.内部类”
6、在抽象类中,如果定义了static属性或方法就可以在没有对象的时候直接调用。
3.6接口
3.6.1接口的定义
接口是一个特殊类,类中只有抽象方法和全局变量(1.8之前)。
1、接口必须有子类,一个子类可以使用implements关键字实现多个接口,避免单继承局限
2、接口的子类必须覆写接口中的全部抽象方法
3、接口的对象可以利用子类对象向上转型进行实例化操作。
//实现接口
interface jiekou1{
public static final String str="HAHAHAH";
public abstract void print();
}
interface jiekou2{
public static final String str="LOLOL";
public abstract void get();
}
class jicheng1 implements jiekou1,jiekou2{
public void print() {
System.out.println("jiekou1");
}
public void get(){
System.out.println("jiekou2");
}
}
public class TestDemo8 {
public static void main(String args[]) {
jiekou1 jc1=new jicheng1();
jiekou2 jk2=(jiekou2)jc1;
jc1.print();
jk2.get();
System.out.println(jc1 instanceof jiekou1);
System.out.println(jc1 instanceof jiekou1);
//虽然jc1既是jiekou1的接口实例又是jiekou2的接口实例,但是jc1只能调用接口1的抽象方法,jk2只能调用接口2的抽象方法
}
}
子类可以继承抽象类的同时,实现接口
class c extends 抽象类 implements 接口1 接口2····
一个抽象类可以继承一个抽象类,或者实现若干接口,但是一个接口不能继承抽象类
3.6.2接口的应用
1、定义标准
2、实现工厂设计模式
如果是自己写的接口,绝对不要使用关键字new直接实例化接口子类,应该使用工厂类来完成
3、实现代理设计模式
3.6.3抽象类与接口的区别
在所有的设计中,接口应该是最先设计出来的
3.7Object类
3.7.1Object类的定义
Object类是所有类的父类。
class Book extends Object{
}
public class TestDemo{
public static void main(String args[]){
//向上转型,接受Book子类对象
Object obja=new Book();
//向上转型,接受String子类对象
Object objb=“hello”;
//向下转型
Book b= (Book)obja;
//向下转型
String s =(String) objb;
}
}
3.7.2取得对象信息
这里最终要的我感觉就是这里这个取得对象信息。
我们在直接输出一个对象时,对于String类,我们发现其输出的是里面的字符串的值,但是当我们定义一个类
class Book extends Object(){
}
public class TestDemo{
public static void main(String args[]){
Object obja=new Book();
System,out.println(obja);
//向上转型,接受String子类对象
Object objb=“hello”;
System,out.println(objb);
}
}
如上代码,输出obja我们得到的是Book@1db9743。输出objb我们得到的是hello。
这就很奇怪,obja与objb都是Object类,输出不应该都是地址吗?
其实,这是因为Object类中有三个覆写方法
toString():取到对象的信息
equals():对象比较
hasCode():取得对象的哈希ma
如果我们定义一个类,没有覆写这三个方法,那就默认为系统的方法,而String类中覆写了toString方法,当取String类对象信息时,他会将其内容输出。
注意:所以在输出一个对象时不管是否主动调用toString(),其最终都是调用toString()将对象信息转换为String进行输出。
3.8宠物商店
在这章有很多概念,类,抽象类,接口,object类等等。下面以一个宠物商店的例子来强化知识。
用户需求,实现宠物商店的增删模糊查询。
首先是宠物类,这个应该被定义一个接口(impl)
其实现接口就是各个具体宠物的类,比如猫狗等。(实现类)
再定义一个宠物商店类,在这个类中,需要定义添加,删除和模糊查询的代码。(controller)
public class PetShop {
public interface pet{
public String getName();
public int getAge();
}
class petshop{
//实现增删模糊查询
private Link pets=new Link();
public void add(pet pet){
this.pets.add(pet);
}
public void delete(pet pet){
this.pets.remove(pet);
}
//根据名字查询
public Link search(String key){
Link result=new Link();
//将集合数组变为Objet数组 应该要保存的是object
//但是真正查询的数据在pet接口对象的getName()方法的返回值
Object ob[]=this.pets.toArray();
for(int x=0;x<ob.length;x++){
pet p=(pet)ob[x];
if (p.getName().contains(key)){
result.add(p);
}
}
return result;
}
}
class cat implements pet{
private String name;
private int age;
public cat(String name,int age){
this.name=name;
this.age=age;
}
public boolean equals(Object ob){
if(this==ob){
return true;
}
if(ob==null){
return false;
}
if(!(ob instanceof cat)){
return false;
}
cat c=(cat) ob;
if(this.name.equals(c.name)&&this.age==age){
return true;
}
return false;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
public String toString(){
return "猫名字"+this.name+"年龄"+this.age;
}
}
}
3.8基本数据类型的包装类
首先,为什么要有基本数据类型的包装类呢?
我们知道,在java中,所有的操作都需要用对象的形式进行描述。但基本数据类型不是对象啊。
int i=1 i*2这不就和java矛盾了吗?
包装类:就是将基本数据类型包装成对象
其实一切他都是有原因的:在JDK1.0开始,java专门给出了一组包装类,来包装8中基本数据类型。
那么这个怎么用呢,引入装箱和拆箱操作
public class Test(){
public static void main(String args[]){
Integer obj=new Integer(10);//将基本数据类型装箱
int temp=obj.intValue();//将基本数据类型拆箱
System.out.println(temp*2);
}
}
哦,原来是这样,但是这也太麻烦了吧!
对,很麻烦,所以在JDK1.5开始,java提供了自动装箱和拆箱的操作
public class Test(){
public static void main(String args[]){
Integer obj=10;//将基本数据类型自动装箱
int temp=obj;//将基本数据类型拆箱
System.out.println(temp*2);
}
}
哦,soga