面向对象程序的三大特性
文章目录
面向对象的三大特性:封装、继承、多态
1.封装
封装简单理解就是屏蔽细节
1.1访问限定符
访问限定符可以决定被修饰的方法或者字段在哪里可以使用和在哪里不能使用
范围 | private | default | protected | public |
---|---|---|---|---|
同一个包的同一类 | 可以 | 可以 | 可以 | 可以 |
同一个包的不同类 | 可以 | 可以 | 可以 | |
不同包的子类 | 可以 | 可以 | ||
不同包的非子类 | 可以 |
- 前面没有修饰限定符时,默认的是default
例如:
这是在Parentclass包中:
public class Test1 {
private int a=1;
protected int b=2;
int c=3;
public int d=4;
}
//这是同一个包下的不同类
class Test2{
public static void main(String[] args) {
Test1 test1=new Test1();
//private 在其他类不能使用
//default、protected 、public可以在同一个包其他类使用
//System.out.println(test1.a);
System.out.println(test1.b);
System.out.println(test1.c);
System.out.println(test1.d);
}
//输出:2 3 4
这是在Subclasses包中创建的:
//这是不用包的子类
class Test4 extends Test1{
public static void main(String[] args) {
Test4 test4=new Test4();
//protected 、public可以在不同包的子类使用
System.out.println(test4.b);
System.out.println(test4.d);
}
}
//输出2 4
//这是不同包不同类
public class Test {
public static void main(String[] args) {
Test1 test2=new Test1();
//只有public可以在不同包的不同类中使用
System.out.println(test2.d);
}
//输出4
1.2 包
1.2.1包的概念
为了更好的管理类,把多个类收集在一起成为一组,称为软件包。
就比如把一些歌曲放到一个文件下,把电影放到另一个文件下。而这些歌曲和电影就相当于是类,而文件就相当于是包。
包是对类、接口等的封装机制的体现,在同一工程中允许存在相同名称的类,只要处在不同包。
1.2.2导入包中的类
1️⃣可以使用java.uti导入包,然后使用包中的类
int []array={1,2,3,4};
//可以直接打印这个数组
System.out.println(java.util.Arrays.toString(array));
2️⃣也可以使用import语句导入,这样不必每次使用都用java.util导入包
import java.util.Arrays;
int []array={1,2,3,4};
//使用Arrays中的类,可以直接导入Arrays包
System.out.println(Arrays.toString(array));
2.继承
2.1继承是什么
在写几个类时,可能这些类之间有许多相同的地方,在面向对象思想中提出了继承的概念,把相同的地方抽取出来,实现代码的复用。
2.2 继承的概念
继承:它允许程序员在保持原有类型的基础上进行扩展,增加新功能,产生的类,称为派生类,对共性的抽取,实现代码的复用。
比如小明和小红都是学生,我们可以对他们的共性进行抽取。
共性是都有年龄、姓名、身高、体重。这些相同的类也叫做父类/基类/超类。
class SameStudent{
String name;
int age;
int height;
float weight;
}
而小明会武术这些特长都是小明自己特有的,小红会游泳,那么这就是特有的类就是子类/派生类。
class XiaoMing{
public void xm(){
System.out.println("KongFu");
}
}
class XiaoHong{
public void xh(){
System.out.println("youyong");
}
}
2.3怎么继承
修饰符 class 子类 extends 父类 { // ...
}
在上述类中:
class XiaoMing extends SameStudent{
public void xm(){
System.out.println("KongFu");
}
}
class XiaoHong extends SameStudent{
public void xh(){
System.out.println("youyong");
}
}
- 子类会把父类的成员变量和成员方法继承到子类中
- 子类继承父类后,要添加自己的成员变量或者成员方法,不然也就没有必要继承了
2.4 父类成员的访问
2.4.1 子类中访问父类的成员变量
1️⃣子类和父类不存在成员变量
class SameStudent{
String name;
int age;
int height;
float weight;
}
class XiaoMing extends SameStudent{
public void xm(){
age=10;//直接访问
System.out.println("KongFu");
}
}
2️⃣子类和父类变量名相同
class SameStudent{
String name;
int age=15;
int height=160;
float weight=50.0f;
}class XiaoHong extends SameStudent{
int age=18;
float height=160.0f;
public void xh(){
//变量名相同时,优先访问子类的
//子类没有的,父类有会访问父类的
//父类和子类都没有的会报错
System.out.println(weight);//50.0
System.out.println(height);//160.0
System.out.println(age);//18
System.out.println("youyong");//youyong
}
}
public class Student {
public static void main(String[] args) {
XiaoHong xiaoHong=new XiaoHong();
xiaoHong.xh();
}
}
3️⃣在子类中访问父类的变量
class SameStudent{
String name;
int age=15;
int height=160;
float weight=50.0f;
}class XiaoHong extends SameStudent{
int age=18;
float height=160.0f;
public void xh(){
//super可以直接访问父类的成员
System.out.println(super.age);//15
System.out.println(age);//18
System.out.println("youyong");//youyong
}
}
2.4.2 访问成员方法
1️⃣方法名称不同时
子类先访问自己的,如果自己没有就访问父类中的方法
2️⃣方法名称相同时
-
如果父类和子类的方法参数不同,会构成重载,编译器会选择合适的方法进行访问
-
参数相同时,优先访问子类的方法
2.5 执行顺序
- 先执行父类和子类的静态,再执行父类的实例、构造,最后执行子类的实例、构造
- super只会访问从父类继承过来的,this优先访问子类自己的,如果自己没有就是访问父类的。
【注意】:一般继承不超过三层,为了防止超过三层,可以用final修饰
3.多态
多态就是多种形态,在不同的对象访问的时候,出现的结果是不同的。
3.1多态的实现的条件
- 必须在继承体系下
- 子类必须对父类中的方法进行重写
- 通过父类的引用调用重写
class SameStudent{
String name;
int age=15;
int height=160;
float weight=50.0f;
public void strengths(){
System.out.println("wu");
}
}
class XiaoMing extends SameStudent{
public void strengths(){
System.out.println("KongFu");
}
}
public static void main(String[] args) {
XiaoMing xiaoMing=new XiaoMing();
xiaoMing.strengths();//输出KongFu
}
3.2重写
3.2.1 重写的特点
-
1.方法名称一样 2.返回值一样(返回值构成父子关系也可以) 3.参数一样
-
private修饰的方法不能重写
-
final修饰的方法也不能重写,final修饰的方法叫做密封方法
3.2.2 重写和重载的区别
区别点 | 重写 | 重载 |
---|---|---|
参数列表 | 不能修改 | 必须修改 |
返回类型 | 不能修改(除非构成父子关系) | 可以修改 |
访问限定符 | 限制不能更狭隘 | 可以修改 |
3.3 向上转型
3.3.1 向上转型
向上转型是创建一个子类的对象用父类的类型来接收
//父类类型 对象名 = new 子类类型()
SameStudent s = new XiaoMing
- 向上转型,相当于把小范围向大范围转换
- 会把子类特有的方法丢掉
- 如果子类重写了父类的方法,向上转型,会让创建的新对象中的方法是子类的方法
class Draw{
public void draw(){
System.out.println("画图");
}
}
class Circle extends Draw{
public void draw(){
System.out.println("画圆");
}
}
public static void main(String[] args) {
Draw d=new Circle();
d.draw();
}
//输出画圆
3.3.2向下转型
向下转型,是创建了想转型的对象之后,需要子类特有的方法了,在进行向下转型,(运用的较少,且不安全)
class Draw{
public void draw(){
System.out.println("画图");
}
}
class Circle extends Draw{
public void draw(){
System.out.println("画圆");
}
public void print(){
System.out.println("360度");
}
}
public static void main(String[] args) {
Draw d=new Circle();
Circle c=new Circle();
c=(Circle)d;
//可以调用子类的特有对象
c.print();
}
3.4 多态的优点
3.4.1便捷
如果我们需要输出一些连续的不同形状,圆形和方形
class Draw{
public void draw(){
System.out.println("画图");
}
}
class Circle extends Draw{
public void draw(){
System.out.println("画圆");
}
}
class Rect extends Draw {
public void draw() {
System.out.println("画矩形");
}
}
public class Test {
public static void Drawings(){
}
public static void Drawing(Draw shape){
shape.draw();
}
public static void main(String[] args) {
//用多态
//创建Draw数组
Draw[]s1={new Circle(),new Rect(),newCircle(),new Rect()};
for (Draw x:s1) {
Drawing(x);
}
//不用多态
Circle c=new Circle();
Rect r=new Rect();
String[] s2={"c","r","c","r"};
for (String x:s2) {
if(x.equals(c)){
c.draw();
} else {
r.draw();
}
}
}
- 如果有更多的类型需要不断地创建else if语句,让代码更加的“圈复杂度”增加,代码在条件分支较多或者循环较多的情况下,会阅读起来比较麻烦。
3.4.2 可扩展能力强
需要增加新的类型,只用添加一个新类就可以使用。比如在形状中添加一个五边形的子类,就可以直接添加,直接使用
3.5 避免构造方法中调用重写的方法
class A{
public A(){
func();
}
public void func(){
System.out.println("A:func()");
}
}
class B extends A{
private int b=1;
public void func(){
System.out.println("B:func()"+" "+b);
}
}
public class Test {
public static void main(String[] args) {
B b=new B();
}
}
//输出B:func() 0
【注意】在上述代码中,创建B的对象时,因为A是他的父类,会直接调用A的构造方法func(),然后直接进入了子类B的方法func,导致D自身没有构造,所以b没有初始化,所以在构造器中尽量不要调用方法,在构造函数内,尽量避免使用实例方法,除了final和private方法。