1.继承
按照翻译来说应该是基于已有的进行扩展,
假设现在有学生和老师两个类,每个类所拥有的属性如下
- Student
- stuId
- stuName
- stuAge
- stuGender
- Teacher
- teaId
- teaName
- teaAge
- teaGender
- teaCourse
从属性上来看,可以发现Student和Teacher有相同的一部分(看的是逻辑含义,不要看变量名),都有id,name,age,gender.如果在Student和Teacher中都定义一次,属于典型的代码重复。为了复用这一部分内容,我们可以把公共的抽取出来组成一个单独的类:Person,然后Student和Teacher分别基于Person做扩展,继承使用的关键字为extends.
public class Person {
private int id;
private int age;
private String name;
private String gender;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public void showInfo(){
System.out.println(""+id + name + age + gender);
}
}
public class Teacher extends Person{
}
Teacher中没有任何内容,但是我们构建一个Teacher对象试试。
public class Test {
public static void main(String[] args) {
Teacher teacher = new Teacher();
//-- Teacher 扩展了Person后,就拥有Person的所有内容
teacher.setId(1);
teacher.setAge(19);
teacher.setName("张三");
teacher.setGender("男");
teacher.showInfo();
}
}
Teacher中没有内容,他的方法是从person那里继承来的,一旦继承关系成立,那Teacher叫做子类,而Person叫做父类,Java中的继承是单继承,从血缘关系上讲,我们只有一个直系父亲一样,对于任意一个子类来说,都有且只有一个父类,继承成功之后,我们可以访问到的只有非私有内容,私有的内容是无法访问的
2.this关键字
this是一个关键字,用于代表当前对象。对于类来收,就是当前类的一个实例,对于方法来说就是当前调用方法的对象。最终指向的都是对象。
this有两个用法
- this.
- this()
2.1this.的用法
public class A {
public void method01(){
System.out.println("this is method01");
this.method02();
//-- 因为当前类,就是本类,所以this可以省略
method02();
}
public void method02(){
System.out.println("this is method02");
}
对象不仅可以调用方法,还可以调用属性
public class B {
public int i = 3;
public static int j = 4;
public void method01(){
System.out.println(this.i);
System.out.println(i);
}
public static void method02(){
System.out.println(B.j);
System.out.println(j);
}
}
2.2this()的用法
首先先引入一个概念,方法签名 = 方法名称+参数列表
this()
一般用于简化构造方法重载!
public class Person {
private int id;
private int age;
private String name;
public Person(){
}
public Person(int age,String name){
//this.age = age;
//this.name = name;
//-- 不再需要写大量的this.XXX = XXX类,直接调用本类的其它构造方法
//-- 小括号的内参数决定所调用的构造方法是哪个!
this(0,age,name);
}
public Person(int id,int age,String name){
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
//-- 我们可以在方法内添加控制语句.实现对取值和赋值进行控制!
if (age < 0){
age = 17;
}
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.3当this遇到继承
牢记:this代表当前如果当前处于父类,那就是父类的对象,如果当前处于子类,那就是子类的对象
public class C {
public int i = 10;
public static void main(String[] args) {
C c = new C();
System.out.println(c.i);
D d = new D();
System.out.println(d.i);
}
}
class D extends C{
public int i = 100;
}
3.static关键字:
static叫做静态,属于类,通过类名.直接进行访问。所以static修饰的内容绝对不可能是局部的
静态只能直接访问静态,想要访问非静态内容必须通过对象来实现
public class E{
int i = 10;
static int j = 20;
public static void main(String[] args) {
}
public static void method01(){
//Non-static field 'i' cannot be referenced from a static context
//System.out.println(i);
//-- 就想访问i怎么办 构建i所属于的对象
E e = new E();
System.out.println(e.i);
System.out.println(j);
}
-
非静态的可以访问全部(静态和非静态)
-
静态是全局的,类的所有实例共享静态的内容
-
非静态属于对象,每个对象私有。一个对象对其非静态属性做了改变,其它对象不受影响
-
int i = 0; static int j = 0; public static void main(String[] args){ E e1 = new E(); E e2 = new E(); E e3 = new E(); e1.j = 30; System.out.println(e3.j) }
静态优先加载:
-
public class F { public F(){ System.out.println("F"); } //--代码块,因为被static修饰,所以也叫做静态代码块。简称静态域 static { System.out.println(123); } public static void main(String[] args) { new G(); } } class G extends F{ static{ System.out.println(456); } public G(){ System.out.println("G"); } }
父类静态,然后是子类静态。然后是父类构造,最后是子类构造
-
static中不能写this,因为staitc是优先加载的,那么在main方法执行时,this所代表的对象还没有创建,所以在static中无法使用this.
4.super关键字:
super是在继承之后才有的关键字。它的用法和this类似,分为两种情况
super.
代表父类对象super()
代表父类构造方法- 此处引入一个概念为Object,Object是Java的根类,在Java中没有显式继承关系时,默认每个类都继承Object
-
public class Teacher { public Teacher(){ //-- super() 指的是父类的构造方法 //-- 方法是可以重载的,具体那一个构造方法 //-- 根据小括号中的参数列表决定。当前小括号内没有参数,无参 //-- 所以调用的是Object的无参构造 super(); } }
super()代表父类构造方法,具体哪个方法,由参数列表决定
-
//-- 继承了Object public class Animal { private String name; public Animal(String name){ super();//-- Object 无参构造 this.name = name; System.out.println("this is animal"); } public String getName() { return name; } public void setName(String name) { this.name = name; } } // 有显式的继承关系,那就是继承Animal 就不继承Object public class Dog extends Animal{ public Dog(String name){ super(name); } }
super.
代表的是父类对象.
public class Animal {
private String name;
public Animal(String name){
this.name = name;
System.out.println("this is animal");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void showInfo(){
System.out.println("name:" + name);
}
}
public class Dog extends Animal{
public Dog(String name){
super(name);
}
public void showDog(){
//-- 使用下父类的showInfo
//-- 这时候的super.代表的就是父类对象!
super.showInfo();
}
}
4.1super()的使用:
super()
只能出现在一个地方,构造方法的第一行。代表调用父类的构造方法,代表调用父类的构造方法
Java中子类只能调用父类的构造方法!并且只能在子类的构造方法中调用,并且必须第一行
public class Hero {
private int hp;
private int mp;
private int attack;
private int defend;
public Hero(){
//-- super()哪怕不写也是存在的。默认隐藏,必须位于构造方法的第一行
//System.out.println("12");
//Call to 'super()' must be first statement in constructor body
//-- 代表调用父类的构造方法。构建父类的对象!
super();
}
//-- 省略了get|set
}
如果父类中没有无参构造怎么办?那子类的super()就会报错,报错原因是没有一个默认的构造方法
public class Hero {
private String name;
private int hp;
private int mp;
private int attack;
private int defend;
public Hero(String name){
//-- super()哪怕不写也是存在的。默认隐藏,必须位于构造方法的第一行
//-- 代表调用父类的构造方法。构建父类的对象!
//super();
this.name = name;
}
}
public class ArcherHero extends Hero{
}
这样写会报错,因为子类继承了Hero调用Hero的无参构造,但是Hero中没有无参构造,所以super()找不到方法,我们的解决方案有两个
- 父类提供一个无参构造
- 覆盖默认的构造方法,主动调用有参构造
-
public class ArcherHero extends Hero{ public ArcherHero(String name){ super(name); } }
因为子类的构造方法中,第一行一定是
super()
所以导致,构造子类时,会先构造它的父类,然后才是子类本身 -
Hero hearo = new Hero(); //-- 实际上先构造了Object,然后再构造了Hero()
4.2 super.的用法
super代表的是父类对象,所以super.就表示为调用父类的属性或者父类的方法。一样的前提必须是非私有
class Hero{
public void method(){
System.out.println("this is hero method");
}
}
public class ArcherHero extends Hero{
public ArcherHero(String name){
super(name);
}
public void callBack(){//callback是随便命名的方法
//-- 使用的是继承到的method
this.method();
//-- 强调的是是否用父类的!
super.method();
}
}
5.方法重写
方法代表的是类型的行为,子类和父类,针对同一种行为可能有不同的体现。也就是子类保持方法声明不变,但是可以改写继承到的方法体。这种写法叫做方法重写。
public class Hero {
private String name;
private int hp;
private int mp;
private int attack;
private int defend;
public Hero(String name){
//-- super()哪怕不写也是存在的。默认隐藏,必须位于构造方法的第一行
//-- 代表调用父类的构造方法。构建父类的对象!
//super();
this.name = name;
}
//--GET|SET
public void goHome(){
System.out.println("父类回城中!");
}
}
public class ArcherHero extends Hero{
public ArcherHero(String name){
//super("弓箭手");
super(name);
}
//-- @Override 注解 表示子类重写类父类的方法!
@Override
public void goHome() {
//super.goHome();
System.out.println("使用回城特效回城!");
}
}
public class HeroTest {
public static void main(String[] args) {
ArcherHero ah = new ArcherHero("艾希");
System.out.println(ah.getName());
ah.goHome();
}
}
5.1强调使用父类的方法体
这就是super.作用所在,我们可以使用super.来强调使用父类的方法体
如果直接写goHome会自己调用自己,形成递归。我们想要的强调的goHome是父类的,不是子类自己的,就必须加上super.
6.引用类型转换(动态多态)
原生类型转换方式我们知道,有自动转换和强制转换,那我们引用的数据类型也是同样如此,我们知道原生的数据类型转换时根据大小,那我们如何判断引用的数据类型大小呢,我们可以这么理解,宠物 和狗,我们可以说狗是宠物,但是不能说宠物是狗。所以重复的覆盖面要比猫大。所以父类的范围大,子类的范围小。那上面的转换规则我们可以修正下
- 小-》大 狗-》宠物 子类-》父类。 自动转换 向上转型
- 大-》小 宠物-》狗 父类-》子类。 强制转换 向下转型
public class Pet {
private String name;
public Pet(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void eat(){
System.out.println("this is animal eat");
}
public void sleep(){
System.out.println("this is animal sleep");
}
}
public class Dog extends Pet{
public Dog(String name){
//-- super使用最多的地方就是这里了!
super(name);
}
}
public class Cat extends Pet{
public Cat(String name){
super(name);
}
}
public class Test {
public static void main(String[] args) {
Dog dog1 = new Dog("狗1号");
//-- 把小的赋值给大的 子类赋值给父类 int-》long
Pet pet = dog1;
//-- 把大的赋值给小的 父类赋值给子类 long-》int
Dog dog2 = (Dog)pet;
}
}
下面一段代码帮助深入理解
public class Test {
public static void main(String[] args) {
Dog dog1 = new Dog("狗1号");
//-- 把小的赋值给大的 子类赋值给父类 int-》long
Pet pet = dog1;
//-- 把大的赋值给小的 父类赋值给子类 long-》int
Dog dog2 = (Dog)pet;
}
public void method01(Dog dog){
}
public void method02(Cat cat){
}
public void method03(Pet pet){
}
}
三个方法分别可以接收什么类型的参数
- method01 -》 只能接收Dog
- method02 -》 只能接收Cat
- method03 -》 可以接收Cat,Dog,Pet
为什么?因为Pet是父类,可以接收所有的子类实例。这样做的意义到底在哪里呢?提示下重写。因为子类可以改写父类的方法体,我们使用父类来接收所有的子类实例。在没有明确对象之前,你不知道要执行的是哪个方法体是什么