概述
java面向对象三大特性,封装、继承、多态。上一节中我们已经学习了有关封装的知识,接下来我们来学习继承。
1.概念:
1.1一种类与类之间的关系
1.2使用已存在的类的定义作为基础建立新类
1.3新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。
1.4满足"A is a B"的关系
2.特点:
2.1利于代码复用
2.2缩短开发周期
3.语法:
3.1 使用extends实现继承
3.2 单一继承,只能有一个父类。(如:狗继承动物,动物称作父类或基类,狗称作子类或派生类)
3.3子类只能继承自父类的非私有成员,父类不可以访问子类的特有成员
3.4语法规则:返回值类型、方法名、参数类型、顺序、个数都要与父类继承的方法相同。
4.继承后的初始化顺序:
父类静态成员——>子类静态成员——>父类对象构造——>子类对象构造
5.学习方法重写的知识,并比较:方法重载与方法重写
* 方法重写
* 1) 有继承关系的子类中
* 2) 方法名相同,参数列表相同(参数顺序、个数、类型),方法返回值相同
* 3) 访问修饰符,访问范围需要大于等于父类的访问范围
* 4) 与方法的参数名无关
* 方法重载:
* 1) 同一个类中
* 2) 方法名相同,参数列表不同(参数顺序、个数、类型)
* 3) 方法返回值、访问修饰符任意
* 4) 与方法的参数名无关
注:方法重写存在,属性重写不存在。
虽然重写仅限于方法上面,但是在子类中,可以定义与父类重名的属性的。
6.访问修饰符
公有的:public 允许在任意位置访问
私有的:private 只允许在本类中进行访问
受保护的:protected 允许在当前类、同包子类/非子类、跨包子类调用。跨包非子类不允许
默认: 允许在当前类,同包子类/非子类调用。跨包子类/非子类不允许调用。
即满足这样的关系
访问修饰符 | 本类 | 同包 | 子类 | 其他 |
---|---|---|---|---|
private | √ | |||
默认 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
7.如何区分调用的是父类的继承父类的方法,还是子类自己重写的方法?
——接下来我们学习和使用super关键字
7.1 super:父类对象的引用
注:1.父类的构造不允许被继承、不允许被重写,但是会影响子类对象的实例化过程。
2.继承后的初始化顺序:
父类静态成员——>子类静态成员——>父类对象构造(属性(赋值)、构造代码块、构造方法)——>子类对象构造(属性(赋值)、构造代码块、构造方法)
7.2 super的使用:
1)子类构造默认调用的父类的无参构造方法;
除了父类和子类的无参构造方法,我们也拥有一个父类和子类的双参构造,但是即使是这样,在我们进行调用的时候,默认的顺序仍然是:父类的静态代码块、子类的静态代码块、父类的构造代码块、父类的无参构造方法、子类的构造代码块、子类的带参构造方法
(注:具体的执行流程,可以在断点调试中详细观察。下面直接贴出运行结果)
package com.susu.animal;
//父类
public class Animal {
private String name = "妮妮";// 昵称
protected int month;// 月份
String species = "动物";// 品种
static {
System.out.println("我是父类的静态代码块");
}
public static int st2 = 23;
private static int st1 = 22;
{
System.out.println("我是父类的构造代码块");
}
// 父类的构造不允许被继承、不允许被重写,但是会影响子类对象的实例化
public Animal() {
System.out.println("我是父类的无参构造方法");
}
public Animal(String name, int month) {
this.name = name;
this.month = month;
System.out.println("我是父类的带参构造方法");
}
package com.susu.animal;
//子类
public class Cat extends Animal{
private double weight;//体重
public static int st3=44;
static{
System.out.println("我是子类的静态代码块");
}
{
System.out.println("我是子类的构造代码块");
}
public Cat(){
System.out.println("我是子类的无参构造方法");
}
public Cat(String name,int month){
System.out.println("我是子类的带参构造方法");
}
package com.susu.animal;
//测试类
public class Test {
public static void main(String[] args) {
Cat one = new Cat();
/*
*
我是父类的静态代码块
我是子类的静态代码块
我是父类的构造代码块
我是父类的无参构造方法
我是子类的构造代码块
我是子类的无参构造方法
*/
Cat two = new Cat("xinxin",12);
/*我是父类的静态代码块
我是子类的静态代码块
我是父类的构造代码块
我是父类的无参构造方法
我是子类的构造代码块
我是子类的带参构造方法*/
}
}
通过以上代码的运行结果,我们可以清楚的看到,不管是子类调用是带参还是无参,都会默认调用父类的无参构造,而不是带参构造。那么如果我们非要调用父类的带参构造呢?下面我们来看第二点。
2)我们可以通过super()调用父类允许被访问的其他构造方法;
我们写一个父类和子类的双参构造,如果想要调用双参构造方法,我们只需要把参数传进来就好。如:super(name,month),当进入子类双参构造时,由于super(name,month)的存在,会进入父类的双参构造,完成我们期待的指定构造方法的调用。
即顺序:父类的静态代码块、子类的静态代码块、父类的构造代码块、父类的带参构造方法、子类的构造代码块、子类的带参构造方法
为了更好的证明这一点,我们把修改后的代码,以及运行结果贴出来:
package com.susu.animal;
//父类
public class Animal {
private String name = "妮妮";// 昵称
protected int month;// 月份
String species = "动物";// 品种
static {
System.out.println("我是父类的静态代码块");
}
public static int st2 = 23;
private static int st1 = 22;
{
System.out.println("我是父类的构造代码块");
}
// 父类的构造不允许被继承、不允许被重写,但是会影响子类对象的实例化
public Animal() {
System.out.println("我是父类的无参构造方法");
}
public Animal(String name, int month) {
this.name = name;
this.month = month;
System.out.println("我是父类的带参构造方法");
}
package com.susu.animal;
//子类
public class Cat extends Animal{
private double weight;//体重
public static int st3=44;
static{
System.out.println("我是子类的静态代码块");
}
{
System.out.println("我是子类的构造代码块");
}
public Cat(){
System.out.println("我是子类的无参构造方法");
}
public Cat(String name,int month){
/* 子类构造默认调用父类无参构造方法
* 可以通过super()调用父类允许被访问的其他构造方法
* super()必须放在子类构造方法有效代码第一行
*/
super(name,month); //this
System.out.println("我是子类的带参构造方法");
}
package com.susu.animal;
//测试类
public class Test {
public static void main(String[] args) {
Cat three = new Cat("xinxin",12);
/**
*
我是父类的静态代码块
我是子类的静态代码块
*
我是父类的构造代码块
我是父类的带参构造方法
我是子类的构造代码块
我是子类的带参构造方法
*/
}
}
以上代码只是在原有代码的基础上,做了一点点的修改,就产生了我们预期的效果。实际上,我只在Cat类的双参构造中,添加了一行语句,super(name,month),从而调用父类允许被访问的其他构造方法。
3)super()必须放在子类构造方法有效代码第一行。
public Cat(String name,int month){
/* 子类构造默认调用父类无参构造方法
* 可以通过super()调用父类允许被访问的其他构造方法
* super()必须放在子类构造方法有效代码第一行
*/
super(name,month);
//放在了第一行。如果放在其他行,如打印语句的下面,会报错。
System.out.println("我是子类的带参构造方法");
}
7.3 super知识的总结:
super:代表父类引用
子类访问父类成员:
-访问父类成员方法 super.print()
-访问父类属性 super.name;
-访问父类构造方法 super();
注:1.子类的构造的过程中必须调用其父类的构造方法
2.如果子类的构造方法中没有显示标注,则系统是默认调用父类无参的构造方法
3.如果子类构造方法中既没有显示标注,且父类中没有无参的构造方法,则编译出错。
4.-使用super调用父类指定构造方法,必须在子类的构造方法的第一行
7.4 比较this和super
this: 当前类对象的引用 | super:父类对象的引用 |
---|---|
-访问当前类的成员方法 -访问当前类的成员属性 -访问当前类的构造方法 -不能在静态方法中使用 | -访问父类的成员方法 -访问父类的成员属性 -访问父类的构造方法 -不能在静态方法中使用 |
public Cat(String name,int month){
/* 子类构造默认调用父类无参构造方法
* 可以通过super()调用父类允许被访问的其他构造方法
* super()必须放在子类构造方法有效代码第一行
*/
//this();
super(name,month); //this
System.out.println("我是子类的带参构造方法");
}
注: 构造方法调用时,super和this不能同时出现。因为super和this有很多的相似之处,二者都要抢占Cat带参构造中的第一行有效代码,因此放一块时,会产生矛盾。如果强制写上去,不管谁在上面,程序都会报错。
8. Object类
1)Object类是所有类的父类
2) 一个类没有使用extends关键字明确标识继承关系,则默认继承Object类(包括数组)
3) Java中的每个类都可以使用Object中定义的方法
以下是子类当中重写父类概率比较高的方法:
/*toString:
* 1、输出对象名时,默认会直接调用类中的toString
* 2、继承Object中的toString方法时,输出对象的字符串表示形式:类型信息+@+地址信息
* 2、子类可以通过重写equals方法的形式,改变输出的内容以及表现形式
*/
/*equals:
* 1、继承Object中的equals方法时,比较的是两个引用是否指向同一个对象
* 2、子类可以通过重写equals方法的形式,改变比较的内容
*/
Object类中的常用方法
方法 | 说明 |
---|---|
toString() | 返回当前对象本身的有关信息,按字符串对象返回 |
equals() | 比较两个对象是否是同一个对象,是则返回true |
hashCode() | 返回该对象的哈希代码值 |
getClass() | 获取当前对象所属的类信息,返回Class对象 |
我们来区分以下代码
代码1: public boolean equals(Object obj)
属于重写Object类的equals方法,因此参数类型是Object,需要强转为Animal类。
代码2: public boolean equals(Animal obj) 限制了传入参数类型是Animal,避免了类型转换有可能出现的异常。但相对于上面重写父类Object的equals方法而言,属于方法的重载。是针对Animal类中的equals方法产生的重载方法。二者之间的区别大家要区分开来。
public boolean equals(Object obj){
if(obj==null)
return false;
Animal temp=(Animal)obj;
if(this.getName().equals(temp.getName()) && (this.getMonth()==temp.getMonth()))
return true;
else
return false;
}
public boolean equals(Animal obj){
if(obj==null)
return false;
if(this.getName().equals(obj.getName()) && (this.getMonth()==obj.getMonth()))
return true;
else
return false;
}
9. final
/* final 修饰class:该类没有子类
public final class final public class
* final 修饰方法:该方法不允许被子类重写,但是可以正常被子类继承使用
* final 修饰方法内局部变量:只要在具体被使用之前进行赋值即可,一旦赋值不允许被修改
* final 修饰类中成员属性:赋值过程:1、定义直接初始化
2、构造方法中赋值
3、构造代码块中赋值
(否则会报错)
*/
ps: java基本数据类型:int float double ...等
java引用数据类型:类、String System 数组...等 eg. Animal a = new Animal();
那么,我们进行思考?像引用数据类型的变量一旦被final修饰后,它的引用地址是否可以发生改变,里面的属性值是否可以发生改变呢?我们可以通过代码来验证。
public void eat(String name) {
final Animal animal=new Animal("xinxin",1);
//
animal=new Animal();
}
代码animal = new Animal(); 会报错,则说明不能修改它的引用。那么属性呢?依然以代码为例。
public void eat(String name) {
final Animal animal=new Animal("xinxin",1);
//
animal=new Animal();
animal.month=1;
animal.name="ss";
}
我们看到,属性是可以被赋值的。综上,用final修饰的引用类型的变量,在实例化之后,就不允许再进行引用地址的修订,但是引用对象的属性值可以根据我们的需要进行修改。
final总结:
1)修饰类表示不允许被继承
2)修饰方法表示不允许被子类重写
- final修饰的方法可以被继承
- 不能修饰构造方法
3)修饰变量表示不允许修改
方法内部的局部变量:在使用之前被初始化赋值即可。
类中成员变量: 只能在定义时或者构造代码块、构造方法中进行初始化设置。
基本数据类型的变量:初始赋值之后不能更改
引用类型的变量:初始化之后不能再指向另一个对象,但指向的对象的内容是可变的。
4)可配合static使用
5)使用final修饰可以提高性能,但会降低可扩展性
10.注解的分类
按照运行机制分:
1) 源码注解: 注解只在源码中存在,编译成.class文件就不存在了。
2) 编译时注解: 注解在源码和.class文件中都存在
3) 运行时注解: 指的是在运行阶段还起作用,甚至会影响运行逻辑的注解。如Spring注解:@Autowired
按照来源分:
1) 来自JDK的注解
2) 来自第三方的注解
3) 我们自己定义的注解
最后
以上就是迷你悟空为你收集整理的java基础三(继承)的全部内容,希望文章能够帮你解决java基础三(继承)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复