继承
D:\code\黑马\code\oop-up-app\src\com\extend
继承的特点
-
子类能继承父类的非私有成员变量和成员方法
-
继承后子类的对象由子类与父类共同组成,每次new出来的对象都是由父类与子类中的所有成员共同组成,所谓的父类中的private无法被继承是指无法访问private修饰的成员,并不是private修饰的成员不存在,就如同单独的一个类中同样无法通过对象去访问它的私有成员
public class Father { public static String Fname = "父类自己的成员"; public int a; private int b; public void Fathermethod() { System.out.println("父类公共方法"); } private void Fathermethod2() { System.out.println("父类私有方法"); } public void setB(int b) { this.b = b; } public int getB() { return b; } }
public class Son extends Father{ public String Sname = "子类自己的成员"; public void Sonmethod() { System.out.println("子类公共方法"); } }
public class Test { public static void main(String[] args) { Son s = new Son(); System.out.println(s.Sname); s.Sonmethod(); System.out.println(Son.Fname);//可以通过类名找父类的静态成员 s.setB(101); System.out.println(s.getB()); // s.b;//会报错,子类不能直接访问父类的私有成员,但是可以通过set,get方法访问 // s.Fathermethod2();//也不可以访问父类的私有方法 s.Fathermethod(); } /* result: 子类自己的成员 子类公共方法 父类自己的方法 101 父类公共方法 */
-
new出的子类对象由子类对象本身和父类共同组成,所占的空间不仅仅有公共成员,也有私有成员。虽然private修饰的父类的私有成员无法直接通过.来访问,但是父类的私有变量同样会在子类对象所存的地址对应的堆内存中开辟空间,可以通过父类方法中的set和get方法来访问
-
调用父类的public方法时,通过地址找到堆内存中类的地址,再通过类的地址找到子类,然后通过子类找到父类
继承的好处
- 减少重复代码的编写
public class People {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Teacher extends People{
private String skill;
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
public void teach(){
System.out.println(getName() + "教授" + skill + "课程");
}
}
public class Consultant extends People{
private int num;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public void consult(){
System.out.println(getName() + "回答了" + num + "次咨询");
}
}
public class Test {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.setName("张三");
teacher.setSkill("Java");
teacher.teach();
Consultant c = new Consultant();
c.setName("李四");
c.setNum(10);
c.consult();
}
}
/*
result:
张三教授Java课程
李四回答了10次咨询
*/
继承的注意事项
-
权限修饰符
-
-
在本类中四种修饰符修饰的方法都可以被访问
-
public class Father { private void printprivate() { System.out.println("private method"); } void printdefault() { System.out.println("default method"); } protected void printprotected() { System.out.println("protected method"); } public void printpublic() { System.out.println("public method"); } //四个方法都可以被调用 public void printall() { printprivate(); printdefault(); printprotected(); printpublic(); } }
-
-
在同一包下的其他类
-
public class Test { public static void main(String[] args) { Father f = new Father(); // f.printprivate(); // 会报错,因为private方法只能在本类中访问 f.printdefault(); f.printprotected(); f.printpublic(); } }
-
-
与Father类在不同包下的子类
-
package com.extend.demo4; import com.extend.demo3.Father; public class Son extends Father { public void printall(){ // printprivate(); // 会报错,因为private方法只能在本类中访问 // printdefault(); // 会报错,因为default方法只能在本包中访问 printprotected(); printpublic(); } }
-
package com.extend.demo4; public class Test { public static void main(String[] args) { Son s = new Son(); // s.printprotected(); // 也会报错,因为虽然s是子类对象,但是并不是子类本身,所以不能访问protected方法 //只有在子类中,才能访问producted修饰的方法 } }
-
-
与Father在不同包下的非子类
-
package com.extend.demo4; import com.extend.demo3.Father; public class Test { public static void main(String[] args) { Father f = new Father(); // f.printprivate(); // 会报错,因为private方法只能在本类中访问 // f.printdefault(); // 会报错,因为default方法只能在本包中访问 // 会报错,因为protected方法只能在本包或其他包的子类中访问 // f.printprotected(); f.printpublic(); } }
-
-
-
单继承
- java是单继承的:一个子类只能继承一个父类,但是一个父类可以被多个子类继承
- A继承了父类B,就不能再继承其他的父类,但是父类B还可以被C继承
- java不支持多继承,但是支持多层继承
- A继承B,B继承C。。。
- java是单继承的:一个子类只能继承一个父类,但是一个父类可以被多个子类继承
-
Object
- Object类是所有类的祖宗,所有类都是Object类的子类或子孙类
方法重写
-
概念
- 当子类觉得父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法1名称,参数列表均与父类一样的方法来覆盖父类的方法
-
注意事项
-
重写后,方法的访问遵循就近原则
-
使用Override注解表示重写
-
子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限(public>protected>缺省)。
重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。 -
私有方法、静态方法不能被重写
public class Fu { public void method() { System.out.println("父类方法"); } }
public class Son extends Fu{ @Override public void method() { System.out.println("子类方法"); } }
public class Test { public static void main(String[] args) { Son son = new Son(); son.method(); } } /* result: 子类方法 */
-
-
应用场景
- 子类重写Object类的toString方法,返回想要的内容,而不再只是对象的地址
- sout(对象) == sout(对象.toString( ))
- 子类重写Object类的toString方法,返回想要的内容,而不再只是对象的地址
子类中访问其它成员的特点
-
在子类方法中访问成员变量和成员方法时,按照就近原则
-
子类方法中访问成员变量优先级:局部变量 > 子类成员变量 > 父类成员变量
-
子类方法中调用重写的方法,会自动调用子类中重写后的
-
如果想访问父类成员,需要用super关键字
-
如果想访问子类成员,需要用this关键字
public class Father { public String name = "父类变量"; public void say() { System.out.println("I am Father"); } }
public class Son extends Father{ public String name = "成员变量"; @Override public void say() { System.out.println("I am Son"); } public void print() { String name = "局部变量"; System.out.println(name); System.out.println(this.name); System.out.println(super.name); say(); super.say(); } }
public class Test { public static void main(String[] args) { Son s = new Son(); s.print(); } } /* result: 局部变量 成员变量 父类变量 I am Son I am Father */
-
子类构造器的特点
-
子类的全部构造器,会先默认调用父类的无参构造器,在执行自己的构造器
class Fu { public Fu() { System.out.println("父类构造器"); } } class Zi extends Fu { public Zi() { super();//无论写不写,默认存在,指向父类的无参构造器 System.out.println("子类构造器"); } } public class Test1 { public static void main(String[] args) { Zi z = new Zi(); } } /* result: 父类构造器 子类构造器 */
-
如果父类没有无参构造器,则必须在子类构造器中显式调用父类的有参构造器
class Fu { public Fu(int a) { System.out.println("父类有参构造器"); } } class Zi extends Fu { public Zi() { super(3);//显示定义 System.out.println("子类构造器"); } } public class test { public static void main(String[] args) { Zi z = new Zi(); } } /* result: 父类有参构造器 子类构造器 */
子类构造器调用父类构造器的常用应用场景
- 需要对父类和子类的成员变量同时进行初始化时
class Person {
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Teacher extends Person {
private String subject;
public Teacher() {
}
public Teacher(String name, String subject) {
super(name);//调用父类的有参构造器进行赋值
this.subject = subject;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
@Override
public String toString() {
return super.getName() + "老师的教学科目是" + this.subject;
}
}
public class Test2 {
public static void main(String[] args) {
Teacher t = new Teacher("张三", "Java");
System.out.println(t);
}
}
/*
result:
张三老师的教学科目是Java
*/
this(参数)可以调用当前类的兄弟构造器
-
在任意类的构造器中,都可以通过this(参数)来调用该类的其它构造器
- 下面代码的需求:如果没有给性别,则默认为男
class People { private String name; private int age; private char sex; public People() { } //使用this来调用兄弟构造器 public People(String name, int age){ this(name, age, '男'); } public People(String name, int age, char sex){ this.name = name; this.age = age; this.sex = sex; } @Override public String toString() { return "People{" + "name='" + name + '\'' + ", age=" + age + ", sex=" + sex + '}'; } } public class Test3 { public static void main(String[] args) { People p1 = new People("张三", 20); System.out.println(p1); People p2 = new People("李四", 21, '女'); System.out.println(p2); } } /* result: People{name='张三', age=20, sex=男} People{name='李四', age=21, sex=女} */