封装
-
理解封装:指将对象的状态信息隐藏在对象内部,不允许外部访问内部信息,而是通过提供的方法实现对内部信息的操作与访问
-
访问控制符:由左向右访问控制级别依次变大
private—》default—》protected—》public
private:当前类访问权限
default:包访问权限
protected:子类访问权限
public:公共访问权限
当不使用任何访问控制符来修饰类和类成员时,系统默认使用default修饰符
-
示例:
public class Student {
//属性私有
private String name;
private int id;
private char sex;
//提供可以操作的方法,public的get和set方法
public String getName(){
return this.name;
}
public void setName(String name){
//合理性校验,用户名不得超过6个字
if(name.length()>6 || name.length()<2){
System.out.println("您设置的用户名不符合规范");
}else {
this.name = name;
}
}
public int getId() {
return this.id;
}
public void setId(int id) {
//同样规范设置
if(id!=8){
System.out.println("id不符合规范");
return;
}else {
this.id = id;
}
}
}
继承(extends)
extends实际意思是扩展:子类是对父类的扩展,子类是一种特殊的父类,Java的子类不能获得父类的构造器
Java只能有一个直接父类,有多个间接父类
class Fruit extends Plant{…}
class Apple extends Fruit{…}
方法重写
public class Bird {
public void fly(){
System.out.println("我是一只鸟,可以飞");
}
}
public class Ostrich extends Bird{
public void fly(){
System.out.println("我是鸵鸟不能飞");
}
public static void main(String[] args) {
Ostrich os = new Ostrich();
os.fly();
}
}
在main方法中os.fly执行的不再是Bird类的fly方法,而是Ostrich中的,这种子类中包含父类同名方法的现象为方法重写(Override),也叫方法覆盖
super限定
- 如果需要在子类中调用父类被覆盖的方法,可使用super限定来调用实例方法,父类类名调用类方法
- 如果子类没有包含和父类同名的成员变量,则无需显式使用super或父类名作为调用者
public class Bird {
public void fly(){
System.out.println("我是一只鸟,可以飞");
}
}
public class Ostrich extends Bird{
public void fly(){
System.out.println("我是鸵鸟不能飞");
}
public void OverrideMethod(){
super.fly();
}
public static void main(String[] args) {
Ostrich os = new Ostrich();
os.fly();
os.OverrideMethod();
}
}
调用父类构造器
子类不会获得父类构造器,但可以调用父类构造器里的初始化代码
在一个构造器中调用另一个重载的构造器使用this,在子类构造器中调用父类构造器使用super
public class Base {
public double size;
public String name;
public Base(double size,String name) {
this.size = size;
this.name = name;
}
}
public class Sub extends Base{
public String color;
public Sub(double size, String name, String color) {
super(size, name);
this.color = color;
}
public static void main(String[] args) {
Sub s = new Sub(2,"ljh","blue");
System.out.println(s.size+s.name+s.color);
}
}
super调用的父类构造器也必须出现在子类构造器的第一行,所以this和super不会同时出现
不管是否适用super调用父类构造器,子类构造器总会调用父类构造器一次:
-
使用super显式调用
-
子类使用this显式调用本类中重载的构造器,系统在执行本类中另一个构造器时即会调用父类构造器
-
子类既无this也无super,系统在执行子类构造器之前会隐式调用父类无参构造器
不仅如此,执行父类构造器时,系统会再次上溯执行父类构造器,以此类推,创建任何java对象,最先执行的都是java.lang.Object类的构造器
多态
向上转型
public class Base {
public int book = 6;
public void base() {
System.out.println("父类的普通方法");
}
public void test(){
System.out.println("父类被覆盖的方法");
}
}
public class Sub extends Base{
public String book = "Java讲义";
public void test(){
System.out.println("覆盖父类的方法");
}
public void sub(){
System.out.println("子类普通方法");
}
public static void main(String[] args) {
Base b = new Base();
System.out.println(b.book);
b.base();
b.test();
Sub s = new Sub();
System.out.println(s.book);
s.sub();
s.test();
Base bs = new Sub();
System.out.println(bs.book);
bs.test();
bs.base();
}
}
- Java允许把一个子类对象直接赋给一个父类变量,无需类型转换,称之为向上转型
- 编译时 = 运行时
- 方法具有多态性,而实例不具备多态性不分编译和运行时,什么类型的变量就是什么类型变量的值
- 引用变量在编译阶段只能调用其编译类型所具有的方法
向下转型
在进行强制类型转换之前,先用instanceof运算符判断是否可以成功转换避免ClassCastException
public class Test {
public static void main(String[] args) {
Person person = new Student();
person.run();
//person.go();
//person无法调用go方法,因为调用时只能调用编译时类型具有的方法,若想调用运行是类型的方法需要强制转换
if(person instanceof Student) {
Student person1 = (Student) person;
person1.go();
}
}
}
组合
为保证父类良好的封装性,通常有如下规则:
- 尽量隐藏父类内部数据
- 不要让子类随意访问,修改父类中的方法
- 尽量不要在父类构造器中调用即将要被子类重写的方法
public class Base {
public Base() {
test();
}
public void test(){
System.out.println("将被子类重写的方法");
}
}
package com.ljh.oop;
public class Sub extends Base{
private String name;
public void test(){
System.out.println("子类重写父类的方法,"+"其name字符串长度为:"+name.length());
}
public static void main(String[] args) {
Sub sub = new Sub();
}
}
上述代码会造成空指针异常!
当系统试图创建Sub对象时,同样会先访问父类构造器,如果父类构造器调用了被其子类重写的方法,则会变成调用重写后的方法。
到底何时需要父类派生新类呢?
- 子类需要额外增加属性,而不仅仅是属性值的修改
- 子类需要增加独有的行为方式
如果时出于类复用的目的,可以使用组合来完成
public class Animal {
private void beat(){
System.out.println("心脏跳动");
}
public void breath(){
beat();
System.out.println("呼吸中");
}
}
public class Bird {
//将父类组合到子类,作为子类的一部分
private Animal animal;
public Bird(Animal animal){
this.animal = animal;
}
//重新定义一个子类的breath方法
public void breath(){
//直接复用Animal中的breath方法来实现Bird中的breath方法
animal.breath();
}
public void fly(){
System.out.println("我是鸟,我会飞");
}
}
public class Wolf {
//组合关键
private Animal animal;
//Wolf类的带参构造
public Wolf(Animal animal){
this.animal = animal;
}
//子类自己的breath方法
public void breath(){
animal.breath();
}
public void run(){
System.out.println("我是狼,我会跑");
}
public static void main(String[] args) {
//需要显式创建被组合的对象
Animal animal = new Animal();
Bird bird = new Bird(animal);
bird.breath();
bird.fly();
Animal animal1 = new Animal();
Wolf wolf = new Wolf(animal1);
wolf.breath();
wolf.run();
}
}
继承要表达的是一种(is a)的关系,组合要表达的是(has a)的关系