- 封装
定义:实现信息隐藏,在java中通过关键字private,protected和public实现封装,利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部联系。外部对象只能通过已经授权的操作来与这个封装的对象进行交流和交互。
好处:良好的封装能够减少耦合;
类内部结构可以自由修改;
可以对成员进行更精确的控制;
隐藏信息,实现细节;
不封装的时候一个实体类是这么写的
public class Husband {
public String name ;
public int age ;
public Wife wife;
}
我们在使用的时候是这么使用的:
public Demo{
public static void mian(String[] args){
Husband husband = new Husband();
husband.age = 30;
husband.name = "张三";
}
}
问题来了,如果哪一天我的年龄需要的是String类型,那么此时如果有几十个上百个类在引用这个实体类,是不是意味着这要修改这几十个类的年龄为String,累不累?
那么封装以后可以怎么做的呢,先看看封装后的这个实体类是怎么样的:
public class Husband {
/*
* 对属性的封装
* 一个人的姓名、年龄、妻子都是这个人的私有属性
*/
private String name ;
private int age ;
private Wife wife;
/*
* setter()、getter()是该对象对外开发的接口
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setWife(Wife wife) {
this.wife = wife;
}
}
现在解决修改年龄为String的解决办法只需要修改一处
public class Husband {
/*
* 对属性的封装
* 一个人的姓名、年龄、妻子都是这个人的私有属性
*/
private String name ;
private String age ;
private Wife wife;
/*
* setter()、getter()是该对象对外开发的接口
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(int age) {
this.age = String.valueOf(age);
}
public void setWife(Wife wife) {
this.wife = wife;
}
}
又比如输入的年龄是1000岁,真把自己当成太子爷(千岁爷)了,这是不符合常识的,这也可以在这个实体类中进行控制,又比如,我们存的是0和1显示的结果是男和女,就可以在get方法里面进行控制。
- 继承
继承描述的是“is a”的关系,如果有两个对象A和B若可以描述为“A是B”,则可以表示A继承B,其中B是被继承者称之为父类或者超类,A是继承者称之为子类或者派生类。
-
- 子类拥有父类非private的属性和方法;
- 子类可以拥有自己的属性和方法,即子类对父类进行扩展;
- 子类可以用自己的方式实现父类的方法;
关于继承必须要说的三个概念:构造器,protected关键字,向上转型
构造器,除了private的方法和属性以外,父类的构造器也是子类继承不了的。对于构造器而言,它只能够被调用,而不能被继承。调用父类的构造方法我们使用super()即可。
构造器的构建过程是从父类开始向子类一级一级地完成构建。编译器会默认给子类调用父类的构造器。但是这个调用父类的构造器是有前提的:父类有默认构造器。如果父类没有默认构造器,我们就要必须使用super()来调用父类构造器(必须在子类构造器第一行代码中声明),否则编译会报错。
protected访问修饰符,在范文范围中是比private,默认大,比public的修饰符小,同包中可以访问,不同包的子类亦可访问。
向上转型,将子类转换成父类,在继承关系上面是向上移动的,所以一般称之为向上转型。由于向上转型是由一个叫专用类型向较通用类型转换,所以它总是安全的,唯一发生变化的可能就是属性和方法的丢失。
继承也是存在缺陷的:父类变,子类就必须变;继承是破坏了封装,对于父类而言,它的实现细节对于子类来说是完全透明的;继承是一种强耦合;
- 多态
指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,尽管是重载该方法。若子类重写了父类中的某些方法,在调用该些方法的时候,必定是使用子类中定义的这些方法(动态连接、动态调用)。实现形式可以是继承也可以是接口,还可以是内部类
public class Wine {
public void fun1(){
System.out.println("Wine 的Fun.....");
fun2();
}
public void fun2(){
System.out.println("Wine 的Fun2...");
}
}
public class JNC extends Wine{
/**
* @desc 子类重载父类方法
* 父类中不存在该方法,向上转型后,父类是不能引用该方法的
* @param a
* @return void
*/
public void fun1(String a){
System.out.println("JNC 的 Fun1...");
fun2();
}
/**
* 子类重写父类方法
* 指向子类的父类引用调用fun2时,必定是调用该方法
*/
public void fun2(){
System.out.println("JNC 的Fun2...");
}
}
public class Test {
public static void main(String[] args) {
Wine a = new JNC();
a.fun1();
}
}
-------------------------------------------------
Output:
Wine 的Fun.....
JNC 的Fun2...
编译时多态,指的是方法的重载,这是静态的重载,而运行时的多态,是通过动态绑定实现的,也就是我们说的多态。
实现多态必要条件:继承(实现)、重写、向上转型,下面是一个很好体现了继承的特点的示例,继承链条末端的优先权最高,越往上越低
public class A {
public String show(D obj) {
return ("A and D");
}
public String show(A obj) {
return ("A and A");
}
}
public class B extends A{
public String show(B obj){
return ("B and B");
}
public String show(A obj){
return ("B and A");
}
}
public class C extends B{
}
public class D extends B{
}
public class Test {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println("1--" + a1.show(b));
System.out.println("2--" + a1.show(c));
System.out.println("3--" + a1.show(d));
System.out.println("4--" + a2.show(b));
System.out.println("5--" + a2.show(c));
System.out.println("6--" + a2.show(d));
System.out.println("7--" + b.show(b));
System.out.println("8--" + b.show(c));
System.out.println("9--" + b.show(d));
}
}
---ouput
1--A and A
2--A and A
3--A and D
4--B and A
5--B and A
6--A and D
7--B and B
8--B and A
9--B and A