构造器
package testClass;
public class Person {
String name;
int age;
//构造器用来生成实例,可以设置一些默认值.
//如果不写出来,编译器也会自动生成一个无参构造器
//构造器和类同名,不能有返回值,前面加一个public
//使用new关键字其实是在调用构造器,并在堆中开辟空间
public Person(){
this.name = "Tom";
}
//有参构造,调用时可以传入参数
//如果写了有参构造,就必须手动写出无参构造
public Person(String name){
this.name = name;
}
//可以有多个构造器
//类里的方法可以互相调用
public Person(String name, int age) {
this(name);//调用其他构造器。当构造器的代码很多时,常用此方法
this.age = age;
}
public void talk(){
System.out.println("我是"+this.name);
}
}
如果一个类只提供了一些方便的函数,不希望用户创建此类的对象,可以提供一个private修饰的无参构造,这样就不能使用构造函数了。
private Math(){}//有了private修饰的无参构造就不能创建对了,因为Math类只提供了一些方便的函数,不需要创建
继承
继承的实现
-
继承的概念
- 继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法
-
实现继承的格式
- 格式:class 子类 extends 父类 { }
- 举例:class Dog extends Animal { }
- 格式:class 子类 extends 父类 { }
-
继承带来的好处
- 继承可以让类与类之间产生关系,子父类关系,产生子父类后,子类则可以使用父类中非私有的成员。
package testClass;
//一个子类可以拥有父类的属性和方法,称为继承
//java不允许多继承,但允许多层继承,不过为了结构的清晰,建议最多继承三层。
public class Student extends Person{
//Person类public的属性,Student对象都可以使用
//Private属性不可以继承
public void study(){
//创建子类的对象时会先调用父类的无参构造器,然后再调用子类的构造器
//可以在子类的构造方法中加上super(),明确要先调用父类的构造器。
super();//如果想要使用父类的有参构造,就加上参数。也可以用super关键字调用父类的其他属性和方法
//如果父类有无参构造,super()可以省略。但是如果父类没有无参构造,必须明确使用super()调用父类有参构造。
super.talk();
System.out.println(this.name+"在学习。");
}
}
下面是测试类:
public class Application {
public static void main(String[] args) {
//类是抽象的,需要实例化创建对象
Student jack = new Student();
jack.name = "Jack";
jack.age = 15;
jack.study();
}
}
继承的好处和缺点
- 继承好处
- 提高了代码的复用性(多个类相同的成员可以放到同一个类中)
- 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
- 继承弊端
- 继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性
- 继承的应用场景:
- 使用继承,需要考虑类与类之间是否存在is..a的关系,不能盲目使用继承
- is..a的关系:谁是谁的一种,例如:老师和学生是人的一种,那人就是父类,学生和老师就是子类
- 使用继承,需要考虑类与类之间是否存在is..a的关系,不能盲目使用继承
继承中构造方法的访问特点
注意:子类中所有的构造方法默认都会访问父类中无参的构造方法
子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化,原因在于,每一个子类构造方法的第一条语句默认都是:super()
问题:如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?
1. 通过使用super关键字去显示的调用父类的带参构造方法
2. 在父类中自己提供一个无参构造方法
推荐方案:
自己给出无参构造方法
继承中成员的访问特点
如果子类定义了和父类完全相同的属性名称时,称为属性的覆盖。当父类子类和方法中都有同名的变量时,优先调用方法中的变量,如果想要使用父类的变量就使用super关键字,想使用子类的变量就使用this关键字。同名方法的调用方式类似。
不过属性的覆盖在实际开发中没有意义。在任何开发中,属性都将使用private封装,一旦封装后属性的覆盖是没有意义的。因为父类定义的私有属性子类根本就看不见,更不会相互影响了。
public class Fu{
String info = "Hello";
}
public class Zi extends Fu{
int info = 30;
public void print(){
int info = 20;
System.out.println(info);//使用方法中的info属性
System.out.println(super.info);//使用父类的info属性
System.out.println(this.info);//使用子类的info属性
}
//测试类
public class Demo{
public static void main(String[] args){
Zi z = new Zi();
z.print();
}
}
this&super
- this&super关键字:
- this:代表本类对象的引用
- super:代表父类存储空间的标识(可以理解为父类对象引用)
- this和super的使用分别
- 成员变量:
- this.成员变量 - 访问本类成员变量
- super.成员变量 - 访问父类成员变量
- 成员方法:
- this.成员方法 - 访问本类成员方法
- super.成员方法 - 访问父类成员方法
- 成员变量:
- 构造方法:
- this(…) - 访问本类构造方法
- super(…) - 访问父类构造方法
方法的重写
当子类定义了和父类的方法名称、返回值类型、参数类型和个数完全相同的方法时,就称为方法的重写。如果子类覆写了父类的方法,而且实例化了子类对象,那么调用的一定是子类的方法。
方法重写注意事项:
- 在覆写的过程中,必须注意到权限问题:被子类覆写的方法不能拥有比父类更严格的访问控制权限。即如果父类的方法是public,那么子类覆写的方法也必须是public;如果父类的方法是default,子类覆写方法是可以是default或public。
- 私有方法不能被重写(父类私有成员子类是不能继承的)
class Fu{
public void fun(){
System.out.println("父类的fun()方法");
}
}
class Zi{
void fun(){//错误!父类方法权限是public,子类不能是default。
System.out.println("子类的fun()方法");
}
}