22面向对象的三大特征
Java面向对象编程02——面向对象的三大特征
封装
-
该露的露,该藏的藏
- 我们程序设计要追求“高内聚,低耦合”。高内聚:就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用
-
封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表达,而通过操作接口来访问,这成为信息隐藏
-
记住这句话就够了:属性私有,get/set
-
封装的意义
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口(getSet)
- 系统的可维护性增加了
-
例子:
学生类
package com.oop.demo04;
//类 private:私有
public class Student {
//属性私有
private String name; //名字
private int id; //学号
private char sex;//性别
private int age;
//提供一些可以操作这个属性的方法
//提供一些public 的get set方法
//get 获得这个数据
public String getName(){
return this.name;
}
//set 给这个数据设置值
public void setName(String name){
this.name=name;
}
//alt+insert 快捷键可以之间生成get/set方法
public int getId() {
return id;
}
public char getSex() {
return sex;
}
public int getAge() {
return age;
}
//可以在set中加以约束,排除不合法的情况
public void setAge(int age) { //不合法
if (age>120||age<0){
this.age=3;
}else{
this.age=age;
}
}
}
main函数程序
public class Application {
public static void main(String[] args) {
Student s1=new Student();
s1.setName("小豪");
System.out.println(s1.getName());
s1.setAge(999);//不合法
System.out.println(s1.getAge());
}
程序运行结果:

继承
- 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
- extends的意思是扩展。子类是父类的扩展
- JAVA中类只有单继承,没有多继承!
- 继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖,组合,聚合等。
- 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
- 子类和父类之间,从意义上讲应该具有“is a”的关系
- object类
- super
- 方法重写
例子:
定义一个人类
public class Person {
// public
//pritected
//default
// private
public int money = 10_0000_0000;// 私有的话无法继承,继承的画一般都是public
public void say(){
System.out.println("说了一句话");
}
}
定义一个学生类并继承人类
package com.oop.demo05;
// 学生 is 人 :派生类,子类
// 子类继承了父类,就会拥有父类的全部方法
public class Student extends Person {
//ctrl + H 会有一个继承树
}
可以使用ctrl + H 会有一个继承树
- 在学生中使用快捷键

- 在人类中使用快捷键

使用Person时,虽然没有继承,但是所有的类都会直接或简介的继承Object类,见上图,就算没有定义方法,也可以调用Object类中的方法

注意:
- 私有的话无法继承,继承的话一般都是public
super
例1:同一个颜色框代表同一个变量,通过super可以继承父类中的属性

程序运行结果:

例2:同一个颜色框代表同一个方法,通过super可以继承父类中的方法

程序运行结果:

注:当方法的修饰符改为private时候,发现子类中super也调用不了了!
例3:无参构造

程序运行结果:

可见,子类在进行执行无参构造时,会有一句隐藏的代码super()默认的来调用父类的无参构造,必须在子类构造器的第一行,同this,所以 二者如同水火不相容,否则会报错(如下):
正确:

错误:

例4. 假设代码中无无参构造只有有参构造,上小节我们讲过,只要有参构造,他就会把无参构造给干掉!

我们会发现子类中,此时根本无法调用无参构造:

super注意点
- super调用父类方法的构造方法时,必须在构造方法的第一个
- super必须只能出现在子类的方法后者构造方法中
- super和this不同同时调用构造方法!
VS this
- 代表的对象不同
- this:本身调用者这个对象
- super:代表父类对象的应用
- 前提
- this:没有继承也可以使用
- super:只能在继承条件下才可以使用
- 构造方法
- this();本类的构造
- super();父类的构造!
补充this
this.方法名称 : 用来访问本类的成员方法
this() : 访问本类的构造方法
- ()中可以有参数的 如果有参数 就是调用指定的有参构造
- 注意事项:
- 1.this() 不能使用在普通方法中 只能写在构造方法中
- 2.必须是构造方法中的第一条语句
重写
当写入A类,B类时,并且定义A类继承了B类
当A类,B类的方法均采用静态的方法时:
A类:
public class A extends B {
@Override//注解:有功能的注解! 重写
public static void test() {
System.out.println("A=>test");
}
}
B类:
public class B {
public static void test(){
System.out.println("B=>test()");
}
}
main函数:
public static void main(String[] args) {
A a =new A();
a.test();//A
//父类的引用指向了子类
B b=new A();
b.test();//B
运行结果:
A=>test()
B=>test()
由此可见:
当类中的方法采用静态方法时,方法的调用只和左边,定义的数据类型有关
当A类,B类的方法均采用非静态的方法时:
A类:
public class A extends B {
@Override//注解:有功能的注解! 重写
public void test() {
System.out.println("A=>test");
}
}
B类:
public class B {
public void test(){
System.out.println("B=>test()");
}
}
main函数:
public static void main(String[] args) {
A a =new A();
a.test();//A
//父类的引用指向了子类
B b=new A;//非静态下,子类重写了父类的方法
b.test();//A
运行结果:
A=>test()
A=>test()
当采用非静态的方法时,才叫重写!!!
以上例子中重写时,子类重写了父类的方法,并且重写可以采用万能键alt+ins进行插入,插入后程序中会有提示(红圆圈标注):

注意:父类的方法重写子类的方法是不存在的!!

总结:
- 需要有继承关系,子类重写父类的方法!
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大,不能缩小;
- 修饰符范围比较:public > Protected >Default > private
- 抛出的异常:范围可以被缩小,但不能扩大
比如,本来继承的别人的东西,继承过来,本来人家欠了100万,你继承过来欠了1000万,肯定是不可以的,只能不断地还,不断地缩小!!!
- 重写时,子类地方法和父类必须一致,方法体不同!!
为什么需要重写?
A:父类的功能,子类不一定需要,或者不一定满足!
多态
- 动态编译:类型 可扩展
- 即同一方法可以根据发送对象的不同而采用多种不同的行为方式。
- 一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多
- 多态存在的条件
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象
- 注意:多态是方法的多态,属性没有多态性。
实例演练
我们新建一个Person类和Student类,其中Student类对Person类进行了继承和son方法重写代码如下:

定义main函数,当我们都调用s1,s2的run时,根据重写的知识,均输出”son“

当我们都调用s1,s2的eat时,会有报错,因为s2的调用类型是person,调用不了我们的eat方法

综上:
- 当父类的引用指向子类调用时,在调用时当子类和父类都有这个方法时,先执行父类的,如果子类对这父类的方法进行了重写,就执行子类的!
- 对象能执行哪些方法主要看左边的类型,和右边关系不大。
student s1 = new student(); //子类指向的引用,调用的是自己的或者是继承父类的!
Person s2 = new student();//父类指向的引用,父类的所有方法,若父类中的方法,子类进行了重写,则先调用子类的
- 当想用s2调用eat方法时,可以采用强转:
((Student) s2)).eat();
多态注意事项:
-
能否执行看引用类型,执行内容看实际类型
-
多态是方法的多态,属性没有多态
-
父类和子类,有联系类型转换异常! classcastException !
-
存在条件:继承关系,方法需要重写,父类引用指向子类对象! Father f1 = new Son();
-
不可以进行重写方法:
- static方法,属于类,它不属于实例
- final常量:
- private方法;
instanceof 和 类型转换
- instanceof(类型转换)引用类型,判断一个对象是什么类型~
instanceof实例
定义三个类Person\Student\Teacher,且三者满足以下的继承关系:
Object->Person->Student
Object->Person->Teacher
则用instanceof 进行判断时:
public static void main(String[] args) {
//System.out.println(x instanceof Y);//能不能编译通过!
//Object->Person->Student
Object object =new Student();
System.out.println(object instanceof Student);//T
System.out.println(object instanceof Person);//T
System.out.println(object instanceof Object);//T
System.out.println(object instanceof Teacher);//F
System.out.println(object instanceof String);//F Object->lang
System.out.println("==========================");
Person person =new Student();
System.out.println(person instanceof Student);//T
System.out.println(person instanceof Person);//T
System.out.println(person instanceof Object);//T
System.out.println(person instanceof Teacher);//F
System.out.println("==========================");
// System.out.println(person instanceof String);//编译报错
Student student =new Student();
System.out.println(student instanceof Student);//T
System.out.println(student instanceof Person);//T
System.out.println(student instanceof Object);//T
System.out.println("==========================");
//System.out.println(student instanceof Teacher);//编译报错 同级不能比较!
// System.out.println(student instanceof String);//编译报错
}
程序运行结果:

由上可见,instanceof 只要画出继承关系的线,毫无关系的或者同级不能比较!
类型转换
- 和数据之间的转换类似:高转低需要强转,低转高不需要。
在子类student中有一个go方法
父类转成子类:
public static void main(String[] args) {
Person obj = new Student();
Student obj1 = (Student) obj;//强制转换
obj1.go();//或者直接((Student) obj).go
}
子类转成父类:
public static void main(String[] args) {
Student obj = new Student();
Person obj1 = obj;//非强制转换
//go方法丢失!
}
注意:
-
父类引用指向子类的对象
-
把子类转换为父类,向上转型;
-
把父类转换为子类,向下转型;强转换
-
方便方法的调用,减少重复的代码!简洁
补充1--Static总结
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(Student.age);//类的的静态属性可以直接调用
// System.out.println(Student.score);//类的的非静态属性不可以直接调用
System.out.println(s1.age);//对象的属性可以直接条用
System.out.println(s1.score);
class中结构

举例:

程序运行结果:

- 静态方法(第一次)--》匿名代码块--》构造方法
- 类加载时,触发静态代码块的执行(仅一次) 执行地位:静态属性初始化之后。 作用:可为静态属性赋值,或必要的初始行为。
- 类加载:

静态导入包
//省略Math.random()前面的Math
//不加static会报错
import static java.lang.Math.random;//静态导入包
public class Test {
public static void main(String[] args) {
System.out.println(random());
}
}
补充2--抽象类
- abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。
- 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
- 抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。
- 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
- 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。
- 一旦有抽象方法这个类一定是抽象类,抽象类里面可以写普通方法
- 类的图标会有两条杠!

我们定义一个抽象类Action
package com.oop.demo08;
// abstract 抽象类:类 单继承 (接口可以多继承)插座
public abstract class Action {
// 约束 有人帮我们实现
//abstract 抽象方法 只有方法名字 没有方法的实现
public abstract void doSomething();
}
定义一个子类A来继承抽象类Action
package com.oop.demo08;
// 抽象类的所有方法,继承了他的子类,都必须要实现他的方法,除非子类也是abstract,让他的子子类实现
public class A extends Action {
@Override
public void doSomething() {
}
}
在定义时候,注意,如果在继承抽象的的时候,系统会有提示,让你对下面的抽象方法及逆行实现,如下:

并且抽象类在main方法中,不能直接new进行新建对象,如下:

并且一旦有抽象方法这个类一定是抽象类
Q:抽象类存在构造器吗?
存在,即使你没有提供任何构造函数,编译器将为抽象类添加默认的无参数的构造函数,没有的话你的子类将无法编译,因为在任何构造函数中的第一条语句隐式调用super()。
通过查看class文件发现:
补充3--接口
- 普通类:只有具体实现
- 抽象类:具体实现和规范(抽象方法)都有!
- 接口:只有规范!自己无法写方法,专业的约束,约束和实现分离,面向接口编程
- 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是...则必须能...”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你好人,则必须干掉坏人;如果你是坏人,则必须欺负好人。
- 接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
- OO的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如c++、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。
接口定义时,前缀为interface,图标为I,如下:


分别定义接口UserService以及TimeService

在调用接口时,可以通过调用多个接口实现多继承

总结
-
约束
-
定义一些方法,让不同的人实现~10--->1
-
接口中方法类型public abstract
-
接口中属性类型public static final
-
接口不能被实例化,接口中没有构造方法
-
implements可以实现多个接口
-
必须要重写接口中的方法~
补充3--内部类
内部类就是在一个类的内部在定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。
1.成员内部类
创建一个外部类Outer
public class Outer {
private int id=10;
public void out(){
System.out.println("这是外部类方法");
}
public class Inner{
public void in(){
System.out.println("这是内部方法");
}
//
public void getID(){
//获得外部类的私有属性
System.out.println(id);
}
}
}
在main函数中调用外部类中内部类的方法,获取外部类中的私有属性
public class Application {
//一个对象的实际类型是确定的
//new student();
// new Person();
public static void main(String[] args) {
//new
Outer outer = new Outer();
//通过这个外部类来实例化内部类
Outer.Inner inner = outer.new Inner();
inner.getID();
}
}
程序运行结果

一个java类中可以有多个cLass类,但是只能有一个public class

2.静态内部类

3.局部内部类
方法中定义的类,可以参考局部变量的理解方法

4.匿名内部类
没有名字的类,没有名字去调用他的方法,不用将实例保存在变量中

接口匿名类

此时只需要重写里面的方法,并返回一个对象!

上面其实就是实现这个接口的类,但这个类也没有名字


浙公网安备 33010602011771号