1. 封装 1.1 封装介绍 封装是面向对象编程的三个基本特征之一,其余两个特征是继承与多态。
封装(encapsulation) 就是将对象的 属性 或 方法 藏在对象的内部被保护起来
程序的其他部分只能通过被授权的方法才能对封装后的 属性 或 方法 进行操作
封装可以隐藏实现细节
把属性封装之后, 只能通过指定方法访问属性, 可以在方法中对数据进行验证
1.2 封装的实现方式
使用 private 修饰符修饰属性或方法
提供一个 public 修饰的 set 方法, 赋予属性值,可在赋予属性值的过程中加入判断逻辑
提供一个 public 修饰的 get 方法, 获取属性值,可在获取属性值的过程中对属性值进行处理
1 2 3 4 5 6 7 8 9 10 11 12 private 数据类型 属性名称;public void setXXX (类型 参数名) { 属性 = 参数名; } public void getXXX (类型 参数名) { return 属性; }
1.3 封装示例 给人设置年龄, 年龄要在一个合理的范围
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class PrivatePerson { public String name; private int age; @Override public String toString () { return "PrivatePerson{" + "name='" + name + '\'' + ", age=" + age + '}' ; } public int getAge () { return age; } public void setAge (int age) { if (age <0 || age > 150 ){ System.out.println("年龄设置错误" ); this .age = 18 ; }else { this .age = age; } } } class PrivatePersonTest { public static void main (String[] args) { PrivatePerson person = new PrivatePerson (); person.name = "阿花" ; person.setAge(1000 ); System.out.println(person.toString()); } }
1.4 封装与构造器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class Private02 { public static void main (String args[]) { PrivatePerson02 p1 = new PrivatePerson02 ("小花" , 2000 ); System.out.println(p1.toString()); } } class PrivatePerson02 { String name; int age; private PrivatePerson02 () { } public PrivatePerson02 (String name, int age) { setName(name); setAge(age); } @Override public String toString () { return "PrivatePerson{" + "name='" + name + '\'' + ", age=" + age + '}' ; } public void setName (String name) { this .name = name; } public String getName () { return name; } public int getAge () { return age; } public void setAge (int age) { if (age <0 || age > 150 ){ System.out.println("年龄设置错误,设置默认 age 为 18" ); this .age = 18 ; }else { this .age = age; } } }
2. 继承 2.1 继承的基本介绍
继承可以提高代码的复用性和扩展性,可以很好的解决代码重复问题,让编程更加接近人类思维
当多个类存在相同的属性和方法时, 可以从这些类中的公共属性和方法抽出来,定义成一个父类
所有的子类只需要通过 extends 来声明继承父类即可,不需要重新定义父类中存在的公共的属性和方法
在继承关系中, 父类又叫做 超类或基类,子类又叫做派生类
Java 中的继承是单继承制,即:子类最多只能直接继承一个父类
Java 中所有的类都是 java.lang.Object 类的子类
继承语法
1 2 3 4 5 6 7 class 子类 extends 父类{ }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public class Extends01 { public static void main (String[] args) { Pupil pupil = new Pupil (); pupil.name = "小红" ; pupil.testing(); pupil.score = 90 ; pupil.getInfo(); Graduate graduate = new Graduate (); graduate.name = "大黄" ; graduate.testing(); graduate.score = 100 ; graduate.getInfo(); } } class Pupil extends Student { public void testing () { System.out.println("小学生 " +name+" 正在考试..." ); } } class Graduate extends Student { public void testing () { System.out.println("大学生 " +name+" 正在考试..." ); } } class Student { String name; int age; double score; public void setScore (double score) { this .score = score; } public void testing () { System.out.println("正在考试ing..." ); } public void getInfo () { System.out.println("Student{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}' ); } }
2.2 继承的细节
子类对象实例化时
不管使用子类的哪个构造器,默认情况下,总会去调用父类的无参构造器
如果父类没有无参构造器,则必须在子类中使用 super 去显式的调用父类的有参构造器完成对父类的初始化工作
如果父类没有无参构造器,也不显式调用父类有参构造器,则编译不会通过
父类构造器的调用不限于直接父类, 将一直往上调用到 Object 类的构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Extends03 { public static void main (String[] args) { Sub03 sub = new Sub03 (); } } class Base03 { String name; private Base03 () { System.out.println("Base03 的无参构造方法被调用" ); } public Base03 (String name) { System.out.println("Base03 的有参构造方法被调用" ); this .name = name; } } class Sub03 extends Base03 { public Sub03 () { super ("123" ); System.out.println("Sub03 的构造方法被调用" ); } }
子类继承了父类所有的属性和方法,
父类 非私有 属性,子类 能够 直接访问
父类的 私有 属性,子类 不能够 直接访问, 需要通过父类中的方法进行访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public class Extends02 { public static void main (String[] args) { Sub02 sub = new Sub02 (); System.out.println(sub.public_); System.out.println(sub.protected_); System.out.println(sub.default_); sub.publicMethod(); sub.protectedMethod(); sub.defaultMethod(); sub.getPrivate(); } } class Base02 { public Base02 () { System.out.println("Base02 的无参构造方法被调用" ); } public String public_ = "public_" ; protected String protected_ = "protected_" ; String default_= "default_" ; private String private_ = "private_" ; public void publicMethod () { System.out.println("public method 被调用" ); } protected void protectedMethod () { System.out.println("protected method 被调用" ); } void defaultMethod () { System.out.println("default method 被调用" ); } private void privateMethod () { System.out.println("private method 被调用" ); } public void getPrivate () { System.out.println("getPrivate 中访问私有方法, private_ = " +private_); privateMethod(); } } class Sub02 extends Base02 { public Sub02 () { System.out.println("Sub02 的构造方法被调用" ); } }
2.3 继承的内存形式 我们看一个案例分析, 当子类继承父类, 创建子类对象时, 内存中发生了什么,
当子类对象创建好以后,建立一种查找关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Grandpa { String name = "Grandpa" ; String hobby="钓鱼" ; } class Father extends Grandpa { String name = "Father" ; int age = 30 ; } public class Son extends Father { String name = "Son" ; public static void main (String args[]) { Son son = new Son (); System.out.println(son.name); System.out.println(son.age); System.out.println(son.hobby); } }
修改图
2.4 继承中的属性查找 属性查找顺序
先看本类是否有该属性,并且可以访问则返回该属性信息
如果子类没有该属性,则去父类找该属性,如果父类有并且可以访问就返回该属性信息
如果父类没有再去父类的父类找, 按照此规则向上一直追溯到 object 类
如果找到父类有该属性,但没有访问权限, 则直接报错,不会再向上查找
3. super super 代表父类的引用, 可用于访问父类的成员属性、成员方法和构造方法
3.1 super访问构造器
如果希望指定去调用父类的某个构造器, 则显式的调用一下, 调用方式 super(参数列表)
使用 super 调用父类的构造器只能在本类构造器中使用
在使用 super 调用父类的构造器时, 必须放在构造器的第一行
使用 super() 和 this() 调用构造器时都只能放在构造器第一行, 因此这两个方法不能共存在一个构造器中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class Super01 { public static void main (String[] args) { Sub01 sub01 = new Sub01 ("小明" ); } } class Base01 { String name; public Base01 () { System.out.println("Base01 父类 的无参构造方法被调用" ); } public Base01 (String name) { this .name = name; System.out.println("Base01 父类 的有参构造方法被调用" ); } } class Sub01 extends Base01 { public Sub01 (String name) { super (name); System.out.println("Sub01 子类 的构造方法被调用" ); } public Sub01 () { super (); System.out.println("Sub01 的构造方法被调用" ); } }
3.2 super访问属性和成员方法 使用 super 可以访问父类中 非私有 的属性和方法,如果想访问父类的私有方法可以借助父类的公有方法
访问父类属性:super.属性名;
访问父类的方法:super.方法名(参数列表);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Super01_A { public String public_ = "public_" ; protected String protected_ = "protected_" ; String default_= "default_" ; private String private_ = "private_" ; public void publicMethod () { System.out.println("public method" ); } protected void protectedMethod () { System.out.println("protected method" ); } void defaultMethod () { System.out.println("default method" ); } private void privateMethod () { System.out.println("private method" ); } } class Super01_B extends Super01_A { public void testSuper () { System.out.println(super .default_); System.out.println(super .protected_); System.out.println(super .default_); super .publicMethod(); super .protectedMethod(); super .defaultMethod(); } }
3.3 super 属性访问顺序
先查找本类, 如果本类有则调用, 如果本类没有则查找父类,
如果父类有则调用, 如果父类没有继续查找父类的父类, 一直找到object类
如果在编码过程中, 尝试调用父类没有访问权限的方法, 则无法通过编译
super访问属性或方法会跳过本类直接去父类查找,如果父类有则直接调用
如果父类没有则去父类的父类继续查找,
如果子类重写了父类的方法,一直找到object类
如果在编码过程中, 尝试调用父类没有访问权限的方法, 则无法通过编译
super 的访问不限于直接父类, 如果爷爷类和本类中有同名的成员,也可以使用,
如果多个基类中都有同名的成员, 遵循就近访问的原则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class Son extends Father { public void call () { m1(); this .m1(); super .m1(); System.out.println("---" ); m2(); this .m2(); super .m2(); System.out.println("---" ); m3(); this .m3(); System.out.println("---" ); m4(); this .m4(); super .m4(); } @Override public void m1 () { System.out.println("Son类 -m1 方法被调用" ); } public void m3 () { System.out.println("Son类 -m3 方法被调用" ); } } class Father extends GrandFather { public void m1 () { System.out.println("Father类- m1 方法被调用" ); } public void m2 () { System.out.println("Father类- m2 方法被调用" ); } } class GrandFather { public void m4 () { System.out.println("GrandFather类- m4 方法被调用" ); } public void m1 () { System.out.println("GrandFather类 - m1 方法 被调用" ); } }
默认访问
使用 this 访问
使用 super 访问
m1()
son.m1 被调用
son.m1 被调用
father.m1 被调用
m2()
father.m2 被调用
father.m2 被调用
father.m2 被调用
m3()
son.m3 被调用
son.m3 被调用
报错,father 没 m3方法
m4()
GrandFather.m4 被调用
GrandFather.m4 被调用
GrandFather.m4 被调用
3.4 Super 与 this 的比较
区别
this
super
访问 属性
访问本类中的属性, 如果本类没有此属性或方法,继续到父类查找, 一直到 object 类
跳过子类, 直接从父类的查找,如果父类没有继续查找父类的父类, 一直到 object 类
调用 方法
访问本类中的属性, 如果本类没有此属性或方法,继续到父类查找, 一直到 object 类
跳过子类, 直接从父类的查找,如果父类没有继续查找父类的父类, 一直到 object 类
调用 构造器
调用本类构造器,必须放在构造器的首行
调用父类构造器,必须放在子类构造器的首行
特殊
表示当前对象
在子类中表示父类对象
4. 方法重写
1 2 3 4 5 6 7 8 9 10 11 12 class Animal { public void say () { System.out.println("动物会叫" ); } } class Dog extends Animal { public void say () { System.out.println("小狗汪汪叫" ); } }
4.1 方法重写细节
子类方法的返回类型和父类方法返回类型一致 或是 父类返回类型的子类
如 父类 方法返回类型是 Object,子类 方法返回类型是 String
子类方法不能缩小父类方法的访问权限
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Sub02 extends Base02 { @Override public String m1 () { return null ; } public Object m2 (String str) { return null ; } private void m3 () { } } class Base02 { public Object m1 () { return null ; } public Object m2 (Object obj) { return null ; } public void m3 () { } }
4.2 方法重载与重写的比较
名称
发生范围
方法名
形参列表
返回类型
修饰符
重载 Overload
本类
必须一样
类型,个数,顺序 至少有一个不同
无要求
无要求
重写 Override
父类,子类
必须一样
必须一样
子类方法返回类型 与 父类方法相同 或者是 父类返回类型的子类
子类方法的访问范围 必须大于等于 父类方法的访问范围