一,封装
1,封装的本质
将一类对象具有的共同的属性和行为写到一个类中。这个过程称为抽象,结果是封装。
2,封装的作用:
1:隐藏了类的具体实现
2:操作简单
3:提高对象数据的安全性
4, 良好的封装能够减少耦合。
3,封裝的实现
(1)通修改属性的可见性来限制对属性的访问(一般限制为private,只有当前类可以访问)
(2)2. 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问,即setter getter方法
e.g;
public class EmployeeDemo {
public static void main(String[] args) {
// 创建对象
Employee jack = new Employee();
// 调用公有方法,给成员变量赋值。
jack.setId("007");
jack.setName("jack");
jack.setGender("男xx");
// 获取实例变量的值
System.out.println(jack.getGender());
System.out.println(jack.getId());
System.out.println(jack.getName());
// 调用成员方法
jack.work();
}
}
class Employee {
private String name;
private String id;
private String gender;
// 提供公有的get set方法
public String getName() {
return name;
}
public void setName(String n) {
name = n;
}
public String getId() {
return id;
}
public void setId(String i) {
id = i;
}
public String getGender() {
return gender;
}
public void setGender(String gen) {
if ("男".equals(gen) || "女".equals(gen)) {
gender = gen;
} else {
System.out.println("请输入\"男\"或者\"女\"");
}
}
public void work() {
System.out.println(id + ":" + name + ":" + gender + " 努力工作中!!!");
}
}
二,继承(描述类与类之间的关系)
1,特点
-
子类拥有父类非private的属性,方法。:父类定义完整的成员 静态成员,非静态,构造方法。静态变量和静态方法都可以通过子类名.父类静态成员的形式调用成功
-
构造函数不能被继承
-
降低类和类之间的代码重复使用就继承,降低对象和对象之间的代码重复使用静态变量
-
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系)。
2,继承的实现
-
使用extends关键字实现类的继承(只能继承一个类,单一继承)
-
使用implements类继承接口,变的实现Java具有多继承的特性(一个类可以继承多个接口)
public interface A { public void eat(); public void sleep(); }//A接口
public interface B { public void show(); } //B接口
public class C implements A,B {}
总结:继承后重载方法可以改变访问修饰符,可有抛出更广的检查异常,返回值类型可以改变。
三,多态(又叫动态绑定)
1本质:(1简单一点)一个引用可以指向多个对象调用多个方法。
class A{ piublic get(){return "PA"} }
class B extends A{ piublic getB(){return "PB"}}
A a=new B();
a.getB(); //引用a指向B类对象,调用B类的方法。
a.get(); //引用a指向A类对象(因为方法只能有对象调用)调用A类的方法。
(2更深一点 )总结动态绑定(多态):动态绑定是指在“执行期间”(而非编译期间)判断所引用的实际对象类型,根据其实际的类型调用其相应的方法。所以实际当中找要调用的方法时是动态的去找的,new的是谁就找谁的方法,这就叫动态绑定。动态绑定帮助我们的程序的可扩展性达到了极致。
2,特点;
- 1. 消除类型之间的耦合关系
- 2. 可替换性
- 3. 可扩充性,可以对所有的而对象进行通用处理。
- 4. 接口性
- 5. 灵活性
- 6. 简化性
3,多态的实现
多态实现的 前提:
- 继承(子类继承父类)
- 重写(子类重写继承父类的方法)
- 父类引用指向子类对象
案例一 (对象转型,向上转型,向下转型)
使用案例二的代码,
向上转型:当父类引用指向子类对象的时候 Animal a=new Cat("catname","blue");除了animal类里面的成 员变量和方法可以访问外,Cat类的新增的成员变量和方法不能被a访问。
向下转型:当需要访问子类的新增成员变量和方法时,需要向下转型此时a是Animal类型的,
Cat c1=(Cat)a;此时就可以用c1访问Cat的新增属性和方法。
总结:对象转型可以使父类对象的引用可以指向子类对象,给程序带来了比较好的可扩展性:我们可以在一个方法的参数里面定义父类的引用,然后实际当中传入参数传的是子类的对象,然后我们再在方法里面判断这个传过来的子类对象到底属于哪个子类,然后再去执行这个子类里面的方法或者调用这个子类里面的成员变量,因此程序的可扩展性比单独定义好多个方法要好一些。
案例二
1 package javastudy.summary;
2
3 class Animal {
声明一个私有的成员变量name。
7 private String name;
10 * 在Animal类自定义的构造方法
13 Animal(String name) {
14 this.name = name;
15 }
16
17 /**
18 * 在Animal类里面自定义一个方法enjoy
19 */
20 public void enjoy() {
21 System.out.println("动物的叫声……");
22 }
23 }
30 class Cat extends Animal {
31 /**
32 * 在子类Cat里面定义自己的私有成员变量
33 */
34 private String eyesColor;
37 * 在子类Cat里面定义Cat类的构造方法
41 Cat(String n, String c) {
42 /**
43 * 在构造方法的实现里面首先使用super调用父类Animal的构造方法Animal(String name)。
44 * 把子类对象里面的父类对象先造出来。
45 */
46 super(n);
47 eyesColor = c;
48 }
49
50 /**
51 * 子类Cat对从父类Animal继承下来的enjoy方法不满意,在这里重写了enjoy方法。
52 */
53 public void enjoy() {
54 System.out.println("我养的猫高兴地叫了一声……");
55 }
56 }
57
117 * 定义一个类Lady(女士
121 class Lady {
122 /**
123 * 定义Lady类的私有成员变量name和pet
124 */
125 private String name;
126 private Animal pet;
127
128 /**
130 * 这个构造方法有两个参数,分别为String类型的name和Animal类型的pet,
131 * 这里的第二个参数设置成Animal类型可以给我们的程序带来最大的灵活性,
132 * 因为作为养宠物来说,可以养猫,养狗,养鸟,只要是你喜欢的都可以养,
133 * 因此把它设置为父类对象的引用最为灵活。
134 * 因为这个Animal类型的参数是父类对象的引用类型,因此当我们传参数的时候,
135 * 可以把这个父类的子类对象传过去,即传Dog、Cat和Bird等都可以。
138 */
139 Lady(String name, Animal pet) {
140 this.name = name;
141 this.pet = pet;
142 }
143
144 /**
145 * 在Lady类里面自定义一个方法myPetEnjoy()
146 * 方法体内是让Lady对象养的宠物自己调用自己的enjoy()方法发出自己的叫声。
147 */
148 public void myPetEnjoy() {
149 pet.enjoy();
150 }
151 }
153 public class Test {
154 public static void main(String args[]) {
155 /**
156 * 在堆内存里面new了一只蓝猫对象出来,这个蓝猫对象里面包含有一个父类对象Animal。
157 */
158 Cat c = new Cat("Catname", "blue");
168 /**
169 * 在堆内存里面new出来一个小姑娘,名字是l1
170 * l1养了一只宠物是c(Cat),
171 * 注意:调用Lady类的构造方法时,传递过来的c,是当成Animal来传递的,
172 * 因此使用c这个引用对象只能访问父类Animal里面的enjoy()方法。
173 */
174 Lady l1 = new Lady("l1", c);
178 * 这三个小姑娘都调用myPetEnjoy()方法使自己养的宠物高兴地叫起来。
179 */
180 l1.myPetEnjoy();
183 }
184 }
执行过程;在main函数中,先有
Cat c = new Cat("Catname", "blue");
这里执行时在new cat对象时它会调用cat的构造方法(用来实例化对象)
Cat(String n,String c){
super(n);
eyesColor=c;
}
注意这里的super()调用父类的构造方法Animal(String name){ this.name = name;}因此会把传过来的字符串“Catname”传递给父类对象的name属性。这时cat对象c中包含有父类对象Animal这里aniaml属性name=Catname.Cat除了拥有从Animal类继承下来的name属性外,还拥有一个自己私有的属性eyesColor,属性值为blue
实际内存中如下;
main函数中接下来执行 Lady l1 = new Lady("l1", c);
这里创建一个Lady对象调用她的构造方法
Lady(String name,Animal pet){
this.name=name;
this.pet=pet;
}
这里把l1和c作为实参传递给了构造方法,接着在构造方法里面执行this.name=name,把传递过来的l1由传给Lady对象的name属性,因此Lady对象的name属性值为l1,这里也把前面new出来的那只Cat的引用c(即Cat类中new的对象c)传递给了构造方法里面的参数pet,接着在构造方法里面执行this.pet=pet,pet参数又把c传过来的内容传递给Lady对象的pet属性,因此pet属性的属性值就是可以找到Cat对象的地址,因此Lady对象的pet属性也成为了Cat对象的引用,通过pet里面装着的值是可以找到Cat对象的,因此pet也指向了Cat,但并不是全部指向Cat,pet指向的只是位于Cat对象内部的Animal对象,这是因为在调用构造方法时,是把c当成一个Animal对象的引用传过来的,把c作为一个Animal对象传递给了pet,所以得到的pet也是一个Animal对象的引用,因此这个pet引用指向的只能是位于Cat对象里面的Animal对象。在我pet引用对象眼里,你Cat对象就是一只普通的Animal,访问你的时候只能访问得到你里面的name属性,而你的eyesColor属性我是访问不到的,我能访问到你的name属性,是因为访问的是位于你内部里面的父对象的name属性,因为我pet引用本身就是一个父类对象的引用,因此我可以访问父类对象的全部属性,而你子类对象Cat自己新增加的成员变量(即属性)我pet引用是访问不了的。不过现在我pet引用不去访问你父类对象的成员变量name了,而是去访问你的成员方法enjoy了。首先是使用Lady对象的引用l1去调用Lady对象的myPetEnjoy()方法,myPetEnjoy()方法定义如下:
public void myPetEnjoy(){
pet.enjoy();
}
然后在myPetEnjoy()方法体里面又使用pet引用对象去调用父类对象里面的enjoy方法,但由于实际上new的对象是cat类型的,所以调用的是子类cat重写的enjoy方法。原因如下:
方法是放在代码区(code seg方法区,静态区)里面的,里面的方法就是一句句代码。因此当使用pet引用去访问父类对象的方法时,首先是找到这个父类对象,然后看看它里面的方法到底在哪里存着,找到那个方法再去执行。这里头就比较有意思了,code seg里面有很多个enjoy方法,有父类的enjoy()方法,也有子类重写了从父类继续下来的enjoy()方法,那么调用的时候到底调用的是哪一个呢?是根据谁来确定呢?注意:这是根据你实际当中的对象来确定的,你实际当中new出来的是谁,就调用谁的enjoy方法,例如
Cat c = new Cat("Catname", "blue");这里实际new出来的是Cat对象,当以参数形式传入之后是animal类型的Lady(String name,Animal pet)
现在动态绑定这种机制指的是实际当中new的是什么类型,就调用谁的enjoy方法。所以说虽然你是根据我父类里面的enjoy方法来调用,可是实际当中却是你new的是谁调用的就是谁的enjoy()方法。即实际当中调用的却是子类里面重写后的那个enjoy方法。当然,讲一点更深的机制,你实际当中找这个enjoy方法的时候,在父类对象的内部有一个enjoy方法的指针,指针指向代码区里面父类的Animal的enjoy方法,只不过当你new这个对象的时候,这个指针随之改变,你new的是什么对象,这个指针就指向这个对象重写后的那个enjoy方法,所以这就叫做动态绑定。动态绑定带来莫大的好处是使程序的可扩展性达到了最好,,也只有动态绑定(多态)这种机制能帮助我们做到这一点——让程序的可扩展性达到极致。因此动态绑定是面向对象的核心,如果没有动态绑定,那么面向对象绝对不可能发展得像现在这么流行,所以动态绑定是面向对象核心中的核心。
案例三
class Father { int x = 1; static int y = 2; void eat() { System.out.println("开吃"); } static void speak() { System.out.println("小头爸爸"); } }
class Son extends Father { int x = 3; static int y = 4;
void eat() { System.out.println("大头儿子很能吃"); }
static void speak() { System.out.println("大头儿子。"); } }
class Demo10 {
public static void main(String[] args) {
Father f = new Son(); // 父类引用指向了子类对象。 System.out.println(f.x); // 1 System.out.println(f.y); // 2 f.eat(); // 输出的是子类的。(非静态方法) f.speak(); // 输出的是父类(静态方法) } } |
总结;
1:当父类和子类具有相同的静态或非静态成员变量,那么在多态下访问的是父类的成员变量
即父类和子类有相同的成员变量,多态下访问的是父类的成员变量(无论静态还还是非静态)。
2:当父类和子类具有相同的非静态方法(就是子类重写父类方法),多态下访问的是子类的成员方法(因为实际new的对象是子类的)。
3:当父类和子类具有相同的静态方法(就是子类重写父类静态方法),多态下访问的是父类的静态方法。
4,当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。