封装就是信息隐藏,只保留一些对外接口使之与外部发生联系。 用户无需知道对象内部的细节,但可以通过对象对外提供的接口来访问该对象。
封装的好处:
用一个经典的例子来说明
//丈夫类
public class Mann {
/**
* 对属性的封装
* 丈夫的私有属性
*/
private String name;
private int age;
private Wife wife;
/**
* 私有对象对外开放的接口,丈夫肯定不能开放getWife接口
*/
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;
}
}
//妻子类
public class Wife {
/**
* 妻子的私有属性
*/
private String name;
private int age;
private Mann man;
/**
* 私有属性对外开放的接口,女人一般不会开放年龄接口
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public Mann getMan() {
return man;
}
public void setMan(Mann man) {
this.man = man;
}
}
解耦 如果我们不使用封装,那么对象就没有setter()和getter(),丈夫类如下:
public class Mann {
public String name ;
public int age ;
public Wife wife;
}
// 我们应该这样来使用它
Mann man = new Mann();
man.age = 30;
man.name = "张三";
如果有一天产品经理不知道吃了什么,突然笑呵呵的叫你把年龄改成String类型,如果有上百个地方都调用了,你不是要崩溃?
如果使用了封装,只需要在封装的这一层转换一下就行了,这是对付需求变更的神器!
private String name ;
private String age ; /* 改成 String类型的*/
private Wife wife;
public String getAge() {
return age;
}
public void setAge(int age) {
//转换即可
this.age = String.valueOf(age);
}
对成员变量进行精准控制
Mann man = new Mann();
man.age = 30; //老妖怪?
// 对setter做一些控制
public void setAge(int age) {
if(age > 120){
System.out.println("ERROR:error age input...."); //提示錯誤信息
}else{
this.age = age;
}
}
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。
通过使用继承我们能够非常方便地复用以前的代码,能够大大的提高开发的效率。 继承就是用来扩展功能,避免修改以前的代码,影响其他功能
this:代表本类对象 super:代表父类空间,不代表父类对象,因为父类没有new instanceof:是二元运算符,左边是对象,右边是类;当对象是右面类或子类所创建对象时,返回true;否则,返回false
当子父类变量同名,用super区分
当子父类中出现成员函数一模一样,会运行子类的函数
当对一个类进行子类扩展,子类需要保留父类的功能,但要有子类特有功能时就使用覆盖完成
class Vehicle{
public void run(){
System.out.println("跑");
}
public void stop() {
System.out.println("停");
}
public Person test() {
return new Person();
}
}
class Horse extends Vehicle{
public void run() {
System.out.println("马儿跑");
}
public Student test() {
return new Student("马儿", 12, "赤兔");
}
}
//父类Person,具有男人女人共有的属性和方法
public class Person {
protected String name;
protected int age = 1;
Person(){
System.out.println("i am Person constructor");
}
}
public class Wife {
/**
* 妻子的私有属性
*/
private Mann man;
/**
* 私有属性对外开放的接口,女人一般不会开放年龄接口
*/
public Mann getMan() {
return man;
}
public void setMan(Mann man) {
this.man = man;
}
}
public class Mann extends Person {
Mann() {
System.out.println("i am Mann constuctor");
}
/**
* 对属性的封装
* 妻子的私有属性
*/
private Wife wife;
/**
* 私有对象对外开放的接口,丈夫肯定不能开放getWife接口
*/
public void setWife(Wife wife) {
this.wife = wife;
}
}
构造器
构建过程是从父类开始向子类一级一级地完成构建,编译器会默认先调用父类的构造器
因为子类的所有构造函数不管有没有参数,第一行都有默认super()
调用父类空参构造函数
public class Person {
protected String name;
protected int age;
Person(){
System.out.println("Person Constrctor...");
}
}
public class Mann extends Person{
private Wife wife;
Mann(){
System.out.println("Mann Constructor...");
}
public static void main(String[] args) {
Husband Mann = new Mann();
}
}
Output:
Person Constructor...
Mann Constructor...
如果父类不是默认构造器,子类则必须用super调用父类构造器,否则报错
所以父类经常多定义一个空参构造器
public class Person {
protected String name;
protected int age = 1;
Person(String name){
System.out.println("i am Person constructor");
}
}
public class Mann extends Person {
private Wife wife;
public Mann(String name, Wife wife) {
super(name);
this.wife = wife;
}
Mann() {
System.out.println("i am Mann constuctor");
} // 报错
}
如果父类构造函数有初始化变量,子类如果不默认访问或强制访问父类构造器,就会丢失变量的值。
子类实例化过程图解
public class SonInstance {
public static void main(String[] args) {
Zi zi = new Zi();
zi.show();
}
}
class Fu{
Fu() {
super();
show();
return;
}
void show() {
System.out.println("fu show....");
}
}
class Zi extends Fu{
int num = 8;
Zi() {
super();
return;
}
void show() {
System.out.println("zi show...."+num);
}
}
//zi show....0
//zi show....8
初始化父类时,子类的num为0 ,还没有赋值,父类初始化完成后才初始化子类
继承树追溯
属性/方法查找顺序:(比如:查找变量h)
查找当前类中有没有属性h
依次上溯每个父类,查看每个父类中是否有h,直到Object
如果没找到,则出现编译错误。
上面步骤,只要找到h变量,则这个过程终止。
构造方法调用顺序:
构造方法第一句总是:super(…)来调用父类对应的构造方法。所以,流程就是:先向上追溯到Object,然后再依次向下执行类的初始化块和构造方法,直到当前子类为止。
注:静态初始化块调用顺序,与构造方法调用顺序一样,不再重复
protected
虽然有的时候子类需要父类的属性,但最好的方式还是将属性保持为private,通过protected方法来控制开放接口的权限,从而类的继承者的访问权限
向上转型
子类可以赋值给父类,就是向上转型,就比如狗是动物,但动物不能是狗,只是子类的属性和方法不能交给父类
public class Mann extends Person {
public void Say(){
System.out.println("world");
}
}
public class Person {
public void Say(){
System.out.println("hello");
}
}
Person p = new Mann("jack");
p.Say(); // world
继承内存模型
简单的画了一下,比较浅显,后面再深入完善