第三章我们详细介绍了Java的类和对象的一些基础内容,下面我们来讲一讲Java的高级类特性。
1.Java的继承性
我们知道,Java的继承性是为了简化代码开发而设定的属性,子类继承了父类之后,子类就直接或者间接拥有了父类的所有属性和方法,无论是不是私有的。有一点需要注意的是,父类的私有化内容,因为Java封装性的原因,子类虽然获取到了父类的私有化内容,但是却无法直接使用,可以通过调用父类公共的方法来间接获取父类的私有化内容。
一个子类只能有一个父类,就好像一个儿子只能有一个‘亲爹’一样;一个父类却可以有多个子类,就好像一个父亲可以有多个‘亲儿子’一样。
Java的继承性是可以传递的,即儿子可以拥有父类的属性和方法,也可以拥有父类的父类的属性和方法,就好像孙子既有亲爹的基因,又有爷爷的基因。
子类是对父类的扩展,而不是父类的子集,所以子类也可以自己定义自己的属性和方法。这个,好像不出来了,大家帮忙想想看。
2.方法的重写
子类继承父类时,若父类的方法对子类来说并不十分适用,子类就可以重写这个方法。重写又称覆盖。
重新的规则:子类方法的返回值类型、方法名称、参数列表必须和父类方法名称一致;子类权限修饰符必须比父类权限修饰符的范围要大于或者等于,子类异常类型的范围必须要小于等于父类异常类型、子类方法与父类方法必须同是非静态或者静态。一个方法就这六种定义内容。
3.super关键字的使用
super的作用主要是为了调用父类的属性和方法(包括构造方法),这样可以有效的将子类当中和父类重名的属性或者方法区分开来,比如我们调用父类中与子类重写的方法同名的方法。
当子类和父类的属性或者方法重名时,用super.属性/方法来调用父类的内容(当同一个类中形参和属性同名时用this.属性/方法来调用当前子类对象的内容)。
子类还可以在子类的构造器中调用父类的构造器,super(形参列表),指定调用的是父类的那个构造方法。this(形参列表)指定调用的是当前类的哪个构造方法。无论是子类的this(形参列表)还是super(形参列表)都必须放在子类构造器的第一行代码上。也就是说,在子类的构造器中,只能够出现this(形参列表)或者super(形参列表)当中的一个。当二者都没有显示的声明调用时,默认调用super()即父类的空参构造器。
4.Java面向对象特性之三--多态性
多态,一种事务的多种表现形态。
多态性在Java上有两种体现
①方法的重载和重写:同名方法可以通过形参列表的不同和子父类的继承关系来同时显示。
②对象的多态性:子类的对象可以赋给父类/父接口的引用。
对象的多态性中,通过父类的引用指向子类的对象实体,因为方法是存在子类对象开辟的堆内存中,所以当调用这个引用变量的方法时,实际执行的是子类对象中存在的重写父类的方法,而不是父类中存在的方法。
通过多态性创建的引用变量对象,只能调用子类重写的父类的方法,是无法直接调用子类特有的方法的。但是我们可以通过向下转型来实现,即将父类引用变量强转为子类类型的变量----(子类)父类变量。向上转型就是我们在对象中用到的多态,即将子类对象赋予父类的引用。注意:子类之间是不可以直接互相强制转型的,即使他们拥有同一个父类,否则回报类型转换异常。
封装性是为了解决私有属性的问题,继承性是为了简化代码开发,那么,多态的存在是为了解决什么问题呢?
多态当然也是为了解决代码简化开发的问题,例如数据库连接中,Java语言只需要提供一个接口就可以,内含抽象的方法,然后各个数据库厂商提供具体的实现类来实现连接不同的数据库的功能即可,这样Java语言就不必针对各个数据库厂商都写一个类来供厂商使用。即将不同的数据库实现类的对象变量当成实参传递给声明接口变量的形参即可。
多态的使用前提是:子类继承了父类或者接口,子类重写了父类或者接口的方法。
需要特别注意的是,Java默认加载父类的与子类同名的属性。所以利用多态声明的父类变量对象,在直接调用属性时,调用的是父类的属性。调用方法时,调用的是子类的重写父类的方法。即方法存在多态,属性不存在多态。
5.所有的类的顶级类Object
Object是Java中所有类的顶级类。
①在Java中,==表示等于,=表示赋值。
当==两侧比较的是基本数据类型时,由基本数据类型的值判断二者是否相等,相等则返回true,不等在返回false。需要注意的是,两侧的基本数据类型即使类型不同,也会返回true,如:int i =65,char j=12;char a ='A';则i==j==a全部返回true。
当两侧是引用数据类型时,两侧比较的是引用变量的地址值,相等返回true,不等返回false。
②equals方法
equals方法只可以处理引用数据类型的变量,在object类中,equals方法仍然是比较两个引用变量的地址值是否相同;所以要想用equals方法比较object类子类的实体内容,就必须要重写object类的equals方法。
③String类在内存中的分析
翻看String类的源代码我们可以知道:它是不可继承的(final修饰类),线程安全的(?????),值不可变(两个成员变量都有final修饰,指针可变),本质上是一个字符数组。
我们知道创建string类对象的时候,一般由三种方式:
- 使用关键字new,如:String s1 = new String(“myString”);
- 直接定义,如:String s1 = “myString”;
- 串联生成,如:String s1 = “my” + “String”
第二种方式直接定义过程:在程序编译期,编译程序先去字符串常量池检查,是否存在“myString”,如果不存在,则在常量池中开辟一个内存空间存放“myString”;如果存在的话,则不用重新开辟空间。然后在栈中开辟一块空间,命名为“s1”,存放的值为常量池中“myString”的内存地址
第三种,改变的不是字符串,而是相当于重新创建了一个新的字符串,重新有一个地址值。
相对于new出来的字符串来说,直接赋值的方式效率好,因为它只在字符串常量池开辟了一个内存空间,而new出来的相当于开辟了两个内存空间,耗费内存。
④toString()方法的使用
当我们打印一个引用变量的对象时,默认会调用这个对象的toString方法。
如果对象所在的类没有重写Object中的toString方法,那么调用的就是Object中的toString方法,打印出全类名+@+首地址值。
6.包装类
将8个基础数据类型包装成类之后,就可以调用类中的方法来处理这些数据了。
基本数据类型、包装类、String类之间的转换问题
原则:转换成谁,去谁里边找转换方法或者构造器。
① 基本数据类型和包装数据类型之间的转换:JDK5.0之后加入了自动装箱和拆箱的功能。
②基本/包装数据类型和String数据类型之间的转换
String-->包装数据类型:Integer.parseInt(str)
包装数据类型-->String:i+“ ”