面向对象特征之二:继承性
一、继承性得到好处:
① 减少了代码的冗余,提高了代码的复用性
② 便于功能的扩展
③ 为之后多态性的使用,提供前提
二、继承性的格式:class A extends B{}
A:子类、派生类、subclass
B:父类、超类、基类、superclass
2.1 体现:一旦子类A继承父类B之后,子类A中就获取了父类B中声明的所有的属性、方法
特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只是因为封装的影响,是的子类不能直接调用父类的结构。
2.2 子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的扩展
子类和父类的关系,不同于子集和集合的关系。
extends:延展、扩展
三、java中关于继承性的规定:
1.一个类可以被多个子类继承
2.Java中类的单继承性:一个子类只能有一个父类
3.子父类是相对概念。
4.子类直接继承的父类,称为:直接父类。间接继承的父类,称为:间接父类
5.子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
四、
1.如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类
2.所有的Java类(除java.lang.Objec类之外)都直接或间接的继承于java.lang.Object类
3.意味着,所有的java类具有java.lang。Object类声明的功能。
继承练习
package exer;
/**
* 定义一个ManKind类,包括
* 成员变量int sex 和 int salary;
* 方法void manOrWoman(),蜂聚sex的值显示“man”(sex == 1)或者“woman”(seax == 0);
* 方法void employeed(),根据salary的值显示“no job”(salary == 0)或者“job”(salary != 0)
*/
public class Mankind {
private int sex;//性别
private int salary;//薪资
public void manOrWoman(){
if(sex == 1){
System.out.println("man");
}else if(sex == 0){
System.out.println("woman");
}
}
public Mankind() {
}
public Mankind(int sex, int salary) {
this.sex = sex;
this.salary = salary;
}
public void employeed(){
// if(salary == 0){
// System.out.println("no job");
// }else if(salary != 0){
// System.out.println("job");
// }
String jobInfo = salary == 0? "no job" : "job";
System.out.println(jobInfo);
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
package exer;
/**
* 定义类Kid继承ManKind,并包括
* 成员你变量int yearsOld;
* 方法printAge()打印yearsOld的值
*/
public class Kids extends Mankind{
private int yearsOld;
public Kids(){
}
public Kids(int yearsOld) {
this.yearsOld = yearsOld;
}
public Kids(int sex, int salary, int yearsOld) {
super(sex, salary);
this.yearsOld = yearsOld;
}
public void printAge(){
System.out.println(yearsOld);
}
public int getYearsOld() {
return yearsOld;
}
public void setYearsOld(int yearsOld) {
this.yearsOld = yearsOld;
}
}
package exer;
/**
* 定义类KidsTest。在类的main方法中实例化Kids对象someKid。用该对象访问其父类的成员变量及方法
*/
public class KidsTest {
public static void main(String[] args){
Kids someKid = new Kids(12);
someKid.printAge();
someKid.setSalary(0);
someKid.setSex(1);
someKid.employeed();
someKid.manOrWoman();
}
}
方法的重写(override/overwrite)
1.重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
2.应用:重写以后,当创建子类的对象以后,通过子类对象调用子父类的同名同参数的方法时,实际执行的是子类重写父类的方法。
3.重写的规定:
方法的声明:权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{//方法体}
预定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
① 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
特殊情况:子类不能重写父类中声明为private权限的方法
③ 返回值类型:
父类被重写的返回值类型是void,则子类重写的方法的返回值类型只能是void
父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
父类被重写的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须是相同的数据类型。
④ 子类重写的方法抛出的异常类型不大于父类被重写的方法跑出的异常类型(具体放到异常处理时候讲)
子类和父类中同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)
面试题:区分方法的重载与重写
四种权限修饰
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | Yes | |||
(缺省) | Yes | Yes | ||
protected | Yes | Yes | Yes | |
public | Yes | Yes | Yes | Yes |
关键字:super
1.supper理解为:父类的
2.super可以用来调用:属性、方法、构造器
3.super的使用:调用属性和方法
3.1 我们可以在子类的方法或构造器中。通过使用“super.属性”或“super.方法”的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们喜欢省略“super.”
3.2 特殊情况:当子类和父类中定义了同名的属性时,我们想要在子类中调用父类中声明的属性,则必须显式的使用“super.属性”的方式,表明调用的是父类中声明的书香
3.3 特殊情况:当子类重写了父类的方法以后,我们想要在子类的方法中调用父类中被重写的方法时,则必须显式的使用“super.方法”的方式,表明调用的是父类中被重写的方法。
4.super调用构造器
4.1 我们可以在子类的构造器中显式的使用“suoper(形参列表)”的方式,调用父类中声明的指定的构造器
4.2 “suoper(形参列表)”的使用,必须声明在子类构造器的首行
4.3 我们在类的构造器中,针对“this(形参列表)”或“suoper(形参列表)”只能二选一,不能同时出现
4.4 构造器的首行,没有显式声明“this(形参列表)”或“suoper(形参列表)”,则默认调用的是父类中空参的构造器:super()
4.5 在类的多个构造器中,至少有一个类的构造器中使用了“suoper(形参列表)”,调用父类中的构造器
子类对象实例化的过程
1.从结果上来看:(继承性)
子类继承父类以后,就获取了父类中声明的属性或方法。
创建子类的对象:在堆空间中,就会加载所有父类中声明的属性
2.从过程来看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用父类的构造器,进而调用父类的父类的构造器,知道调用到java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类的结构,子类才可以考虑进行调用。
明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建了一个对象,即为new的子类对象。
面向对象特征之三:多态性
1.理解多态性:可以理解为一个事物的多种形态。
2.何为多态性:
对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
3.多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 — 虚拟方法调用
有了对象的多态性以后:我们在编译期,只能调用父类中声明的方法。但在运行期,我们执行的是子类重写父类的方法。
总结:编译,看左边;运行,看右边
4.多态性的使用前提:① 类的继承关系 ② 子类重写了父类的方法
5.对象的多态性:只适用于方法,不适用于属性(编译和运行都看左边)
package java4;
/**
* 多态性的使用举例:
*/
public class AnimalTest {
public static void main(String[] args){
AnimalTest test = new AnimalTest();
test.func(new Dog());
test.func(new Cat());
}
public void func(Animal animal){
animal.eat();
animal.shout();
}
//没有多态性则需分别写对应方法
// public void func(Dog dog){
// dog.eat();
// dog.shout();
// }
// public void func(Cat cat){
// cat.eat();
// cat.shout();
// }
}
class Animal{
public void eat(){
System.out.println("动物,进食");
}
public void shout(){
System.out.println("动物:叫");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗吭骨头");
}
@Override
public void shout() {
System.out.println("汪!汪!汪!");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
@Override
public void shout() {
System.out.println("喵!喵!喵!");
}
}
instanceof 操作符
加入Man类是Person类的子类
多态:Person p1 = new Man();
要将p1要赋值给Man类的对象,可使用强制类型转化实现(向下转型):Man man1 = (Man)p1;
instanceof关键字的使用:
a instanceof A:判断对象a是否是类A的实例。如果是,返回true,否则,false
使用情景:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就可以进行向下转型,否则不能。
如果 a instanceof A返回true,则a instanceof B也返回true,其中类B是类A的父类。
//练习:
//Man和Women都是Person的子类
//问题一:编译时通过。运行时不通过
Person p1 = new Women();
Man m1 = (Man)p1;
Person p2 = new Person();
Man m2 = (Man)p2;
//问题二:编译时通过,运行时也通过
Object obj = new Women();
Person p = (Person)obj;
//问题三:编译不通过
Man m3 = new Woman();
1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,:编译看左边,运行看右边
2.对于示例变量则不存在这样的现象,即使子类里定义了与父类完全一样的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量:编译运行都看左边
//多态性练习
class Base{
int count = 10;
public void display(){
System.out.println(this.count);
}
}
class Sub extends Base{
int count = 20;
public void display(){
System.out.println(this.count);
}
}
public class FieldMethodTest{
public static void main(String[] args){
Sub s = new Sub();
System.out.println(s.count);//20
s.display();//20
Base b = s;
// ==,对于引用数据类型来讲,比较的是两个引用数据类型变量的地址值是否相同
System.out.println(b == s);//true
System.out.println(b.count);//10
b.display();//20
}
}
Object类的使用
1.Object类是所有java类的根父类
2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
3.Object类中的功能(属性、方法)就具有通用性。
属性:无
方法:equals() / toString() / getClass() / hashCode()/ clone()/ finalize()/ wait()/ notify()/ notifyAll()
4.Object类中只声明了一个空参构造器
面试题, == 和 equals() 区别
一、回顾 == 的使用
== : 运算符
1.可以使用在基本数据类型变量和引用数据类型变量中
2.如果比较的是基本数据类型变量,比较两个变量保存的数据是否相等。(不一定类型要一样)
如果比较的是引用数据类型:比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体
二、equals()方法的使用:
1.是一个方法,而非运算符
2.只适用于引用数据类型
3.Object类中equals()的定义:
public boolean equals(Object obj){return (this == obj)}
说明:Object类中定义的equals()和==作用是相同的:比较两个对象的地址值是否相同。
4.像String、Data、File、包装类等都重写了Object类的equals()方法,重写以后,比较的是两个引用对象的“实体内容”是否相同
5.通常情况下,我们自定义的类如果使用equals()的话,通常是比较两个对象的“实体内容”是否相同。那么我们就要重写equals()方法
public class EqualsTest {
public static void main(String[] args){
int i = 10;
int j = 10;
double d = 10.0;
System.out.println(i == j);//true
System.out.println(i == d);//true
boolean b = true;
char c = 10;
System.out.println(i == c);//true
char c1 = 'A';
char c2 = 65;
System.out.println(c1 == c2);//true
Test test1 = new Test();
Test test2 = new Test();
System.out.println(test1 == test2);//false
System.out.println(test1.equals(test2));//false
String str1 = new String("aaa");
String str2 = new String("aaa");
System.out.println(str1.equals(str2));//true
}
}
class Test{
}
Object类中toString()的使用
1.当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
2.Object类中toSting()的定义:
public String toString() {
return this.getClass().getName() + "@" + Integer.toHexString(this.hashCode());
}
3.像String、Date、File、包装类等都重写了Object类中的toString()方法。
使得在调用对象toString()时,返回“实体内容”信息
4.自定义类也可以重写toString()方法,当调用此类方法时,返回对象的“实体内容”
Java中的JUnit单元测试
1.下载jar包(junit.jar、hamcrest-core.jar)
https://github.com/junit-team/junit4/wiki/Download-and-Install
2.打开 File --> Project Structure ->Modules-> 在Dependencies 下添加jar包
3.+ --> Library… --> java --> 选择jar的路径添加。
4.建一个test文件夹和src同级,并右键–>Mark Directory As --> Test Sources Root(将其设置为测试文件夹)
5.选择需要测试的代码,按Ctrl + Shift + T创建单元测试类,Cmd + Shift + T作为类和测试之间的导航
Java类的要求:① 此类是public的 ②此类提供公共的无参的构造器
单元测试类:权限是public,没有返回值,没有形参
6.写测试代码,运行测试类
执行顺序:
几个常用的注解:
- @Test:把一个方法标记为测试方法
- @Before:每个测试方法执行前自动调用一次
- @After:每一个测试方法执行完自动调用一次
- @BeforeClass:所有测试方法执行前执行一次,在测试类还没有实例化就已经被加载,所以用static修饰
- @BeforeClass:所有测试方法执行前执行一次,在测试类还没有实例化就已经被加载,所以用static修饰
- @Ignore:暂不执行该测试方法
包装类的使用
1.java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量也具有类的特性
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
2.掌握的:基本数据类型、包装类、String三者之间的转化
import junit.framework.TestCase;
public class WrapperTest extends TestCase {
//基本数据类型 -->包装类、调用包装类的构造器
public void test1(){
int num1 = 10;
Integer int1 = new Integer(num1);
System.out.println(int1.toString());
Integer int2 = new Integer("123");
System.out.println(int2.toString());
Float f1 = new Float(12.3F);
Float f2 = new Float("12.3");
System.out.println(f1);
System.out.println(f2);
Boolean b1 = new Boolean(true);
Boolean b2 = new Boolean("TrUe");
System.out.println(b2);//true
boolean b3 = new Boolean("true123");
System.out.println(b3);//false
Order order = new Order();
System.out.println(order.isMale);//false
System.out.println(order.isFemale);//null
}
//包装类 --> 基本数据类型:调用包装类的xxxValue()
public void test2(){
Integer int1 = new Integer(12);
int i1 = int1.intValue();
System.out.println(i1 + 1);
}
/**
* JDK 5.0新特性:自动装箱与自动拆箱
*/
public void test3(){
//自动装箱
int num1 = 10;
Integer in1 = num1;//自动装箱
//自动拆箱
System.out.println(in1.toString());
int num2 = in1;//自动拆箱
}
//基本数据类型、包装类 --> String类型
public void test4(){
int num1 = 10;
//方式1:连接运算
String str1 = num1 + "";
//方式2:调用String的valueOf(Xxx)
float f1 = 12.3f;
String str2 = String.valueOf(f1);//12.3
}
//String类型 --> 基本数据类型、包装类
public void test5(){
String str1 = "123";
//错误的情况:
// int num1 = (int)str1;
// Integer in1 = (Integer)str1;
int num1 = Integer.parseInt(str1);
System.out.println(num1 + 1);
String str2 = "true";
boolean b1 = Boolean.parseBoolean(str2);
System.out.println(b1);
}
}
class Order{
boolean isMale;
Boolean isFemale;
}
关于包装类使用的面试题
import junit.framework.TestCase;
/**
*/
public class InterviewTest extends TestCase {
public void test1(){
Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1);//1.0
}
public void test2(){
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);//false
//Integer内部定义了IntegerCache结构,IntegerCache定义了Integer[],
//保存了从-128~127范围的证书,如果我们使用自动装箱的方式,给Integer赋值的范围在
//-128~127范围内时,可以直接使用数组中的元素,不用再去new了。目的:提高效率
Integer m = 1;
Integer n = 1;
System.out.println(m == n);//true
Integer x = 128;//相当于new了Integer对象
Integer y = 128;//相当于new了Integer对象
System.out.println(x == y);//false
}
}