一、封装
封装就是隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别,通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。对象是封装类的实例,比如张三是人,人是一个封装类,张三只是对象中的一个实例、一个对象)。比如我们日常生活中的小兔子、小绵羊都可以封装为一个类。
- 封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,以特定的访问权限来使用类的成员。
- 一旦类的实现者修改了代码(例如把 name 改成 myName), 那么类的使用者就需要大规模的修改自己的代码, 维护成本较高,封装后就从不能随意的修改
class Student {
//限定你只能在类当中进行访问
private String myName ; //----------如果一个属性或者方法被private修饰,就只能在类当中使用
private int age;//---------无特殊要求 所以的属性都设置为私有的
public void setMyName ( String myName) { //-------------提供一个公开的接口让外部设置 被private修饰的MyName
// MyName = MyName;-------------------错误,其实就是 自己给自己赋值 并没有赋值属性 局部变量优先,形参
this.myName = myName; //---------- 正确this引用,表示调用该方法的对象
} //或者当前对象的引用
public String getMyName ( ) {//-------------提供一个公开的接口让外部获取 被private修饰的MyName
return this.myName;
}
public void func1 () {
System.out.println("func1 ( )");
}
public void show() {
System.out.println("我叫" + this.myName + ", 今年" + this.age + "岁"); //----------被private修饰 ,类内是可以访问的
}
//重新 实现了一下 Object类的 toString()方法
//Object 是 所有类的父类 alt + insert ->toString()
// @Override //注解:这个注解指的是 这个方法是重新写的
// public String toString() { // -------------可自动生成
// return "Student{" +
// "myName='" + myName + '\'' +
// ", age=" + age +
// '}';
// }
}
public class TestDemo {
public static void main(String[] args) {
Student student = new Student();
// student.MyName= "baozi"; --------------------不可以被访问,只能在类中访问 private修饰了
student.setMyName("baozi"); //-----------使用public接口设置被private修饰的成员变量 public接口不会被轻易修改,一般较稳定
String str = student.getMyName(); //-----------public提供了接口 获取private成员变量的值
System.out.println(str);
student.show(); //-----------------成员变量修改后 看一下 show一下 看
// System.out.println(student);// 自动打印 alt+ insert 选toString
}
}
二、继承
继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。
- 使用 extends 指定父类.
- Java 中一个子类只能继承一个父类 (而C++/Python等语言支持多继承).
- 子类会继承父类除了构造方法外的所有
- 对于父类的 private 的字段和方法,只是拥有,但子类中是无法访问的.
- 子类的实例中, 也包含着父类的实例. 可以使用 super 关键字得到父类实例的引用.
- final 修饰的类不可继承
class Animal {
protected String name ;//-----成员属性 name protected 体现了封装与继承 注意:不同包中只有子类才可以访问,相同包随意
public Animal ( String name ) {
this.name = name;
System.out.println("Animal ----------构造器 "+ this.name );
}
public void eat() { //成员方法
System.out.println(this.name + "Animal-----------eat( )");
}
private void sleep() { //成员方法
System.out.println("Animal-----------sleep()");
}
}
class Cat extends Animal { //------猫继承了动物除去构造方法外的所有
public Cat ( String name ) {
//必须放在构造器的第一行 和this一致
super( name ); /**对于继承而言,子类会默认调用父类的构造器,但是如果没有默认的父类构造器,子类必须要显示的调用父类的构造器,而且必须是在子类构造器中做的第一件事(第一行代码)*/
//显示调用了父类的构造方法 不是继承
super.name = "super 可以访问父类的属性";
super.eat(); //访问父类的方法
System.out.println("cat ----------构造器 ");
}
}
//被final修饰的类是不能被继承了(密封类)
final class BaoZi extends Cat { //多层继承不能超过3层 继承不可以多重
public BaoZi (String name) {
super(name);
}
}
class Bird extends Animal {
public Bird(String name) {
super( name );
}
public void fly( ) {
System.out.println(this.name+"Bird-----------fly( )");
}
}
public class TestDemo {
public static void main(String[] args) {
Cat cat = new Cat( "baozi");
cat.name = "包子"; //猫继承了name属性
cat.eat(); //继承了吃 方法
//cat.sleep(); /** 如果private修饰的方法 ,是可以被继承的 但是子类中无法访问 */
}
}
三、多态
多态同一个行为具有多个不同表现形式或形态的能力。是指一个类实例(对象)的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性
实现多态必须
①父类引用引用子类对象
②父类和子类有同名的重写方法
③通过父类的引用调用这个重写的方法时候 (运行时候绑定)
- 封装是让类的调用者不需要知道类的实现细节;多态能让类的调用者连这个类(Shape)的类型是什么都不必知道, 只需要知道传过去的对象(cycle)具有某个方法即可.因此, 多态可以理解成是封装的更进一步, 让类调用者对类的使用成本进一步降
- 可扩展能力更强,使用多态的方式代码改动成本也比较低
class Shape { // 关注父类的代码, 就能够同时兼容各种子类的情况
public void draw ( ) {
}
}
class Cycle extends Shape {
@Override // 方法是被重写的 防止重写的时候失误出现命名错误等 提前报错
public void draw() {
System.out.println("○");
}
}
class Rectangle extends Shape {
@Override
public void draw() {
System.out.println("口");
}
}
class Flower extends Shape {
@Override
public void draw() {
System.out.println("♣");
}
}
public class Test {
public static void drawShape (Shape shape ) { //不关心shape这个引用引用了那个对象 只要重写了draw方法 就会发生运行时绑定
shape.draw(); /** 此时shape调用了哪个draw方法 不知道 取决于它引用的是哪个对象,
指向的对象不一样,调用的draw方法也不一样,
draw方法因为引用不同对象表现形式不一样 这样就是多态*/
}
public static void main(String[] args) {
Shape shape1 = new Cycle(); //向上转型
Shape shape2 = new Rectangle();
Shape shape3 = new Flower();
drawShape ( shape1);
drawShape(shape2);
drawShape(shape3);
}
}
举个例子
对于, 计算机的CPU/内存/主板/独显/光驱/打印机 有很多功能(方法/行为), 那么到底哪些东西是继承, 哪些东西是接口呢.
首先, cpu/内存/主板 是从大型机开始都必备的, 任何计算机都不能把它们去掉.
所以, 这三样东西是继承的, 也就说笔记本的cpu/内存/主板是继承自微机(PC)的
但是/光驱/呢, 现实上很多超薄笔记本不需要光驱的功能.
如果光驱做成继承, 那么笔记本就必须具有光驱, 然后屏蔽光驱功能, 那么这台笔记本还能做到超薄吗? 浪费了资源.
所以光驱,打印机这些东西就应该做成插件.
然后, 在笔记本上做1个可以插光驱和打印机的接口(usb接口).
也就是说, PC的派生类, 有些(笔记本)可以不实现这个接口, 有些(台式机)可以实现这个接口,只需要把光驱插到这个接口上.
至于光驱是如何实现的,
例如一些pc派生类选择实现蓝光光驱, 有些选择刻录机. 但是usb接口本身并不关心. 取决与实现接口的类.
这个就是现实意义上的多态性.