第五章:类与类的继承(应该是非常重要的一章)
面向对象的基本概念
面向对象: 把涉及到的对象抽离出来,然后考虑对象之间的关系构建整个项目,面向对象的设计是一种提供符号设计系统的面向对象的实现过程。面向对象是以领域对象开发为指导。
举例说明(可能不太正确):
把大象装进冰箱
面向过程的思维:
- 打开冰箱
- 装进大象
- 关闭冰箱
面向对象的思想:
- 冰箱打开
- 冰箱存储
- 冰箱关闭
下面,我们从对象,开始了解上图所有内容,
1.对象的定义
含义
对象是类的实例,拥有具体的状态行为。例如Student是类(描述同类对象共同的状态和行为),而对象是指一个具体的Student个体,对象是动态的,每个对象都拥有一个从创建,运行到消亡的动态过程。同时对象也同样占内存空间,存储对象的状态数据,即成员变量。(比如文海同学<对象>的身高<成员变量>)
//定义方法:类名 对象名; //或者类名 对象名 = new 类名( 参数列表 ); Student a; Student a = new Student();
组成.
和类中成员变量以及方法有关(好比一个人,有多种属性:姓名、性别、年龄、体重等,都有多种行为:吃饭、走路等)对象=属性+行为
模板
.
没啥好说的,模板就是类,那么介绍一下类
什么是类:类不是一个实体的存在,比如手机这个类,手机并不是实体,如iPhone7 才是一个实体,手机并不是,类只是一个模子,确定对象将会有的特征(属性)和行为(方法)
类的组成: 属性和方法(如果你是从上看到这,就不会有对此处的疑问)
类的定义方法:
定义类名 public class 类名{ //[初始化模块](https://www.cnblogs.com/wp456/p/3233186.html "初始化模块")的定义【初始化模块首先被执行】 //定义属性(成员变量)的部分 编写类的属性 属性1的类型 属性1; 属性2的类型 属性2; .......... 属性n的类型 属性n; //定义方法(行为)的部分; 编写类的方法 方法1; 方法2; ........ 方法n; }
例如:手机类的定义:
点击查看代码
public class Telphone {//1.定义一个类 //2.定义属性(成员变量) float screen;//屏幕 float cpu;//cpu大小 float mem;//内存大小 //3.定义方法 void call(){ System.out.println("Telphone有打电话的功能!"); } void sendMessage(){ System.out.println("screen:"+ screen + "cpu:"+ cpu + "mem:"+ mem +"Telphone有发短信的功能"); } }
成员变量与局部变量的区别和c差不多,就不过多赘述了,但是修饰符需要重点掌握
特点
- 封装:封装是一种信息屏蔽技术,通过封装讲对象的状态和行为结合成一个独立的模块,尽可能隐藏对象的内部细节(包括对象内部的私有状态以及行为的具体实现)。封装的目的在于把对象的设计者和使用者分开,作为使用者不需要了解对象内部的实现细节,只需要使用设计者提供的行为方法实现功能即可。
private 关键字
是一个权限修饰符。
用于修饰成员(成员变量和成员函数)
被私有化的成员只在本类中有效。
常用之一:
将成员变量私有化,对外提供对应的set ,get方法对其进行访问。提高对数据访问的安全性。
private :私有,是一个权限修饰符。用于修饰成员。
私有的内容只在本类中有效。
注意: 私有仅仅是封装的一种体现。
- 继承:继承表示类之间的层次关系。继承关系使得子类的对象可以共享父类对象的状态(属性<非私有成员变量>)和行为(方法<除构造方法外>)
例如:
父类:Telphone
点击查看代码
package HOME9; public class Telphone { //2.定义属性(成员变量) float screen;//屏幕 private float cpu;//cpu大小 float mem;//内存大小 //3.定义方法 void call(){ System.out.println("Telphone有打电话的功能!"); } void sendMessage(){ System.out.println("screen:"+ screen + "cpu:"+ cpu + "mem:"+ mem +"Telphone有发短信的功能"); } }
子类:iPhone 点击查看代码
package HOME9; public class iPhone extends Telphone { }
测试:
public static void main(String[] args) { iPhone apple = new iPhone(); //子类可以继承父类非private属性(成员变量)和方法(非构造方法) //apple.cpu =5;如果有此行,则报错‘The field Telphone.cpu is not visible’ apple.mem = 6; apple.screen = 7; apple.call(); apple.sendMessage(); }
结果:
多态:多态性是指同名的行为方法可在不同类中具有不同的实现。在子类继承父类的同时,类的方法实现可以进行扩充或者修改,使子类的同名方法更适合子类的对象
多态的实现的必要条件:(重点要记下)
- 存在继承关系
- 存在方法重写
- 父类引用指向子类对象
多态的优点:
- 简化了代码
- 提高了维护性和扩展性
例如:
点击查看代码
public class DuoTaiDemo01 { public static void main(String[] args) { Man m = new Doctor(); m.cut(); m = new Director(); m.cut(); m = new Hairdresser(); m.cut(); } } class Man { public void cut() { System.out.println("我是man, 我也不知道怎么cut"); } } class Doctor extends Man { public void cut() { System.out.println("动手术"); } } class Director extends Man { @Override public void cut() { System.out.println("暂停"); } } class Hairdresser extends Man { @Override public void cut() { System.out.println("剪头发"); } }
重点来了,拿笔记下
多态访问成员的特点:
Father father = new Son()
左边类型 ————右边类型
成员变量:
- 编译时期看左边的类型,如果左边类型中没有变量,编译报错
- 运行时期看左边类型,左边类型的变量的值就是运行的结果
- 编译看左边,执行看左边
成员方法:
编译看左边,执行看右边
构造方法:
1.多态访问子类构造方法会先访问父类构造方法
2.帮助子类初始化父类继承过来的成员
静态方法:
编译看左边,执行看左边
举例代码如下:
package HOME9; public class DuoTaiDemo02 { public static void main(String[] args) { Fu fu = new Zi(); System.out.println(fu.num); // 10 fu.method(); fu.show(); } } class Fu { int num = 10; public void method() { System.out.println("Fu.method()"); } public static void show() { System.out.println("Fu.show"); } } class Zi extends Fu { int num = 20; @Override public void method() { System.out.println("Zi.method()"); } public static void show() { System.out.println("Zi.show"); } }
结果:
向上转型(自动转换)
格式:<父类型> <引用变量名> = new <子类型>();
特点:
子类转为父类 父类的引用指向子类对象。可以理解为自动进行类型转换(和自动类型转换完全是两个概念)
此时通过父类引用变量调用的方法是子类覆盖或继承父类的方法
此时通过父类引用变量无法调用子类特有的属性和方法
解决方法(instanceof + 向下转型)
向下转型(强制转换)
格式:<子类型> <引用变量名> = (<子类型> )<父类型的引用变量>;
特点:
父类转为子类,父类引用转为子类对象。可以理解为强制类型转换
在向下转型的过程中,如果没有转换为真实子类类型,会出现类型转换异常
异常名称: 类型转换异常 java.lang.ClassCastException
产生原因: 在向下转型的过程中,没有转换成真实的类型
解决办法: 在每次向下转型之前做一个类型的判断
类型判断的语法: instanceof
左边对象 instanceof 类名 这个表达式的结果是boolean类型
测试它左边的对象是否是它右边的类的实例
多态的弊端可以使用instanceof关键字+向下转型来解决
我们知道我们需要对父类的所有子类做逐一判断,违背了开闭原则
为了开闭原则我们还是可以继续开发,但是如果这个父类引用是Object呢?
无法做逐一个判断,安全隐患一致存在,可以考虑是泛型。举例1代码如下所示:
package HOME9; public class DuoTaiDemo02 { public static void main(String[] args) { Car c = new Benz(); c.run(); c = new BYD(); c.run(); if (c instanceof Benz) { Benz benz = (Benz) c; benz.leakOli(); } else if (c instanceof BMW) { BMW b = (BMW) c; b.fillOil(); } else if (c instanceof BYD) { BYD byd = (BYD) c; byd.electric(); } Object obj = new BMW(); } } class Car { public void run() { System.out.println("Car.run()"); } } class BMW extends Car { @Override public void run() { System.out.println("BMW.run()"); } public void fillOil() { System.out.println("加油"); } } class Benz extends Car { @Override public void run() { System.out.println("Benz.run()"); } public void leakOli() { System.out.println("漏油"); } } class BYD extends Car { @Override public void run() { System.out.println("BYD.run()"); }、*------*、 public void electric() { System.out.println("充电"); } }
结果:
第六章:多态与内部类
多态(第五章写的有点多了,这章就不写了):
主要内容(向上转型,向下转型,instanceof运算符)
抽象类和抽象方法
抽象类:是对具体类的抽象,用来完成类框架的共享的公共设计,具体子类继承并扩展公共设计(自认为像多态),
抽象类不能实例化对象
一、抽象(abstract)的使用
当父类的某些方法不确定时,可以用abstract关键字来修饰该方法[抽象方法],用abstract来修饰该类[抽象类]。
我们都知道,我们都知道,父类是将子类所共同拥有的属性和方法进行抽取,这些属性和方法中,有的是已经明确实现了的,有的还无法确定,那么我们就可以将其定义成抽象,在后日子类进行重用,进行具体化。这样,抽象类也就诞生了。
例如,定义了“动物”父类,其中“动物名称”和“动物年龄”属性已经明确了,但是“动物叫”的方法没有明确,此时就可以将“动物叫”定义为抽象方法。
所以,抽象类是为了把相同的但不确定的东西的提取出来,为了以后的重用。定义成抽象类的目的,就是为了在子类中实现抽象类。
例子:
package javastudy; public class AbstractDemo1 { public static void main(String[] args) { // TODO Auto-generated method stub } } // 这就是一个抽象类 abstract class Animal { String name; int age; // 动物会叫 public abstract void cry(); // 不确定动物怎么叫的。定义成抽象方法,来解决父类方法的不确定性。抽象方法在父类中不能实现,所以没有函数体。但在后续在继承时,要具体实现此方法。 } // 抽象类可以被继承 // 当继承的父类是抽象类时,需要将抽象类中的所有抽象方法全部实现。 class cat extends Animal { // 实现父类的cry抽象方法 public void cry() { System.out.println("猫叫:"); } }
接口
接口与抽象类似,是对类的一种抽象,指明了类所具备的功能,接口描述的是一种能力。例如组织一次会议有接待参与人员的需求。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口与类的区别和接口的特性
点这->Java 接口我没看明白)
课本上的解释:抽象类更主要的是体现了类模板的设计思想,定义其子类的通用特性及部分已近实现的共有功能。抽象类与子类实现了“一般到特殊”之间的关系(
多态是不是一种方法,而抽象类就是实现此方法的手段,包括接口也是??)
接口中的默认方法(default)
public interface MyInterface { // 普通接口方法 default void defaultMethod() { // 默认方法 } }
为什么需要默认方法
在Java8之前,接口只能有抽象方法。如果不强制所有实现类创建新方法的实现,就不能向现有接口添加新功能。
Java8新增默认方法的原因非常明显。
在一个典型的基于抽象的设计中,一个接口有一个或多个实现类。接口要新增方法,所有的实现都要新增这个方法的实现。否则就不满足接口的约束。
默认接口方法是处理此问题的解决方案。在接口添加默认方法不需要修改实现类,接口新增的默认方法在实现类中直接可用。
例如:
点击查看代码
interface MobilePhone { /** * 获取手机品牌 */ String getBrand(); /** * 获取手机颜色 */ String getColor(); /** * 获取手机长度(毫米) */ Double getLength(); /** * 设置手机时间 */ default String setTime(String newTime) { return "time set to " + newTime; } /** * 对getLength方法进行拓展,返回厘米为单位的长度 */ default String getLengthInCm() { return getLength() / 10 + "cm"; } }
点击查看代码
public class DefaultTests implements MobilePhone { @Override public String getBrand() { return "iphone"; } @Override public String getColor() { return "red"; } @Override public Double getLength() { return 150.00; } @Test public void defaultTest() { System.out.println(setTime("8:00 am")); System.out.println(getLengthInCm()); } }
结果:
接口中的静态方法
接口中的静态方法和类中定义的静态方法一样,不属于特定对象,所以它们不是实现接口的api的一部分,必须使用InterfaceName.staticMethod来调用它们。
为了理解静态方法如何在接口中工作,让我们看一个实例:
interface NewInterface { // 静态方法 static void hello() { System.out.println("Hello, New Static Method Here"); } // 抽象方法 void overrideMethod(String str); } // 实现类 public class InterfaceDemo implements NewInterface { public static void main(String[] args) { InterfaceDemo interfaceDemo = new InterfaceDemo(); // 调用接口静态方法 NewInterface.hello(); // 调用被覆写后抽象方法 interfaceDemo.overrideMethod("Hello, Override Method here"); } // 实现接口方法 @Override public void overrideMethod(String str) { System.out.println(str); }
为什么接口要支持静态方法
接口中的静态方法背后的思想是提供一种简单的机制,允许通过将相关的方法内聚在接口中,而不必创建新的对象。
抽象类也可以做同样的事情。主要的区别在于抽象类可以有构造函数、成员变量和方法。
推荐把和只和接口相关的静态utility方法放在接口中(提高内聚性),而不需要额外创建一些utility类专门去放置这些方法。
内部类
Java 一个类中可以嵌套另外一个类,语法格式如下:
class OuterClass { // 外部类 // ... class NestedClass { // 嵌套类,或称为内部类 // ... } }
要访问内部类,可以通过创建外部类的对象,然后创建内部类的对象来实现。
嵌套类有两种类型:
- 非静态内部类
- 静态内部类
非静态内部类
非静态内部类是一个类中嵌套着另外一个类。 它有访问外部类成员的权限, 通常被称为内部类。
由于内部类嵌套在外部类中,因此必须首先实例化外部类,然后创建内部类的对象来实现。
实例:
点击查看代码
class OuterClass { int x = 10; class InnerClass { int y = 5; } } public class MyMainClass { public static void main(String[] args) { OuterClass myOuter = new OuterClass(); OuterClass.InnerClass myInner = myOuter.new InnerClass(); System.out.println(myInner.y + myOuter.x); } }
以上实例执行输出结果为:
私有的内部类
内部类可以使用 private 或 protected 来修饰,如果你不希望内部类被外部类访问可以使用 private 修饰符:
实例:
点击查看代码
class OuterClass { int x = 10; private class InnerClass { int y = 5; } } public class MyMainClass { public static void main(String[] args) { OuterClass myOuter = new OuterClass(); OuterClass.InnerClass myInner = myOuter.new InnerClass(); System.out.println(myInner.y + myOuter.x); } }
报错:
错误提示信息’The type OuterClass.InnerClass is not visible‘
静态内部类
静态内部类可以使用 static 关键字定义,静态内部类我们不需要创建外部类来访问,可以直接访问它:
实例:
点击查看代码
class OuterClass { int x = 10; static class InnerClass { int y = 5; } } public class MyMainClass { public static void main(String[] args) { OuterClass.InnerClass myInner = new OuterClass.InnerClass(); System.out.println(myInner.y); } }
以上实例执行输出结果为:
好像还有一个匿名内部类,看不懂