java封装,继承,多态详解

一,封装

1,概念

将数据与操作数据有机结合起来,影藏对象的属性和实现细节,仅对外公开实现接口和对象进行交互。

2,四种访问权限概述

public:不同包中的非子类

protected:可访问不同包中的子类

默认权限:包访问权限,可访问同一包中的不同子类

private:类访问权限

一般情况下,建议成员变量用provide修饰,而成员方法用public修饰(当然,这只是一个偷懒的方法,具体情况还要具体分析)

3,包

(1)导入包

一般使用import来导入java自带的包:

import static java.lang.Math.pow;
import static java.lang.Math.sqrt;

double result = sqrt(pow(3, 2) + pow(4, 2));
System.out.println(result);

在使用sqrt,pow时,需要导入包,两者都属于java.lang.Math这个包,sqrt,pow又分别属于Math包下的pow包和sqrt包。当然这里也可以直接写为以下这种形式,*则代表Math中的所有包,但也要注意这种写法,会出现两个不同包下有分别有一个名称相同的子包,这时编译器则不知道这个相同名称的子包是哪个包里面的。

import static java.lang.Math.*;

导入包时也可写为这种写法:int [] array=new int[]{1,2,3,4,5};
                        System.out.println(java.util.Arrays.toString( array));

但这种写法会显得代码冗杂,推荐第一种写法。

(2)自定义包

一般自定义包前有package,例如:

包名的写法:

1,包名以小写字母来书写

2,域名逆置写法,例如想要建立如下的包时,应写为com.baidu.www

4,static成员

1,如果一个对象用static修饰的话,那么这个成员变量就不属于这个对象,而在方法区里,生命周期伴随类的一生。

补充“方法区”:JVM里面包含了五大区,分别为:Java虚拟机,本地方法栈,堆(储存对象),方法区,程序计算器(储存下一条指令)

2,static修饰的变量为类变量,不属于某个对象,是所有对象共享的,且只有一份。

注:静态变量不依赖于对象。故静态方法中不需要对象,也不能在静态方法中调用对象,若真的需要再静态方法中调用对象,可以通过实例化一个新对象来实现。

3,访问静态成员变量时,可通过对象的引用访问,也可用过类名访问,推荐使用类名访问,因为静态成员变量是类类型变量。

4,静态方法:是类方法,类方法中不能访问非静态修饰的成员变量。

5,代码块

(1)普通代码块

普通代码块;定义在方法中的代码块,直接使用{}来定义。

public static void main2(String[] args) {
        {
            //定义在方法里面的,称为普通代码块
        }
}

(2)构造代码块

构造代码块:又称为实例代码块,非静态代码块,一般用于初始化成员变量

(3)静态代码块

静态代码块:用static修饰的代码块,只被执行一次。

例如:
.....
public Student(String name, int age) {
    this.name = name;
    this.age = age;
    System.out.println("带参数的构造方法");
}
{
    System.out.println("构造代码块");
    this.age=15;
}
static{
    System.out.println("静态代码块");
    classroom="二年级三班";
}实例化两个对象:
Student student1=new Student("lihua",12);
Student student2=new Student("xiaofang",11);
......

运行结果:
静态代码块
构造代码块
带参数的构造方法
构造代码块
带参数的构造方法

通过上述例子可得出:

1,当实例化有多个对象时,静态代码块只执行一次,有多个静态代码块时按顺序执行

2,构造代码块优先于构造方法(静态代码块>实例化代码块>构造方法)

6,内部类

可以将一个类定义在另一个类或者一个方法的内部, 前者称为内部类。内部类也是封装的一种体现。

定义在A 类中的B类,此时B类称之为内部类,A类称之为外部类。

(1)实例内部类

class OuterClass{
    public int data1=1;
    public int data2=2;
    public int data3=3;

    class InnerClass{
        public int data1=100;
        private int data5=5;
        public static int data6=6;

        public void test2(){
            System.out.println("InnerClass");
             System.out.println(this.data1);
             System.out.println(OuterClass.this.data1);
             System.out.println(data2);
             System.out.println(data3);
             System.out.println(data5);
             System.out.println(data6);
         }
    }
public void test1(){
        System.out.println("OuterClass");
    }
}

1,实例化内部类时,要先实例化外部类,再用外部类对象的引用去调用内部类

OuterClass out=new OuterClass();
OuterClass.InnerClass innerClass=out.new InnerClass();
或者写为:
OuterClass.InnerClass innerClass1=new OuterClass().new InnerClass();

2,当内部类与外部类的某一个成员变量相同时,内部类会优先调用内部类的成员变量,所以该语句System.out.println(this.data1);的运行结果为100。如果此时想要访问外部类相同名称的成员变量时,应写为<外部类类名.this.成员变量>;例如: System.out.println(OuterClass.this.data1);

(2)静态内部类

class OuterClass1{
    public int data1=1;
    public int data2=2;
    public static int data3=3;

    static class InnerClass{
        public int data1=100;
        private int data5=5;
        public static int data6=6;

        public void test1(){
            System.out.println("InnerClass");
            System.out.println(new OuterClass().data2);
            System.out.println(data3);
            System.out.println(data5);
            System.out.println(data6);
            //静态的不能访问外部类的成员变量
        }
    }
    public void test1(){
        System.out.println("OuterClass");
    }
}

1,静态内部类可访问外部静态成员变量

2,静态内部类的实例化的写法为(相当于静态内部类作为一个外部类的静态成员方法,所以用<外部类名称.静态类名称>):

OuterClass1.InnerClass innerClass=new OuterClass1.InnerClass();

3,静态内部类不能直接访问外部类的成员变量,如果要访问可在内部类内部实例化一个外部类的对象 ,例如:System.out.println(new OuterClass().data2);

(3)局部内部类

class OuterClass2 {
    public int data1 = 1;
    public int data2 = 2;
    public static int data3 = 3;

    public void test(){

        class InnerClass{
            public int data1=100;
            private int data5=5;
            public static int data6=6;

            public void test(){
                System.out.println(data1);
            }

        }

        InnerClass innerClass=new InnerClass();
        innerClass.test();
        //局部内部类只能在当前方法中使用
    }
}

局部内部类为,在外部类的成员方法中定义一个类,因此局部内部来只能在成员方法中使用。实例化也需要在该成员方法中进行。

(4)匿名内部类

interface ITestable{
    void test();
}

public class Text5 {
    public static void main(String[] args) {
        new ITestable(){
            @Override
            public void test() {
                System.out.println("test()");
            }
        }.test();
    }

    public static void main1(String[] args) {
        ITestable it=new ITestable(){
        @Override
        public void test() {
            System.out.println("test()");
        }//并没有实例化接口//相当于一个类实现了这个接口同时重写了test这个方法
    };
        it.test();

    }
}

匿名内部类多用于接口

new ITestable(){
    @Override
    public void test() {
        System.out.println("test()");
    }此处并没有实例化接口,相当于有一个类实现了这个接口同时重写了test()这个方法,又因为这个类没有具体的名称,所以是一个匿名类。

此时想要访问成员变量有两种方法:第一种,在实例化的对象后面直接< .访问的对象>,第二种,写出实例化的引用,<引用 . 访问对象>

二,继承

1,基本概念

它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承主要解决的问题是:共性的抽取,实现代码复用。

2,基本语法

以猫和狗举例,猫和狗有共性部分,例如都具有名字,年龄等。因此可以建一个类,这个类里面包含了猫狗类中共性的部分,我们将其称为父类,而猫狗我们称之为子类,我们让子类继承于父类,可有效地减少代码量。

1,子类继承于父类,我们用关键字extends。写法为<子类extends父类>,一子类可继承多个父类,可写为<子类extends父类1,父类2...>(补充:子类又可称之为派生类,父类又可称之为基类,超类)

2,访问成员变量时,会优先访问子类,若子类没有则会访问父类,若父类也没有,则会报错。

若子类与父类的成员变量名称相同时,会遵循上述原则,先访问子类,但若此时想访问父类,则需要使用关键字super

class Base{
    public int a=1;
    public int b=2;
}

class Derived extends Base{
    public int a=100;
    public int c=3;
        public void test(){
        System.out.println(super.a);
        System.out.println(this.a);
        System.out.println(this.b);
        System.out.println(this.c);
    }
}
遵循优先访问子类,在访问父类的原则,第一个:运用了super访问了父类的a,为1,第二个,访问子类,为100,第三个先访问子类,子类没有,在访问父类,为2,第四个,访问子类,为3.

4,this与super

不同点:

(1)this访问成员变量,super访问父类成员

(2)this调用方法,super调用父类方法

class Base{
    public void test1(){
        System.out.println("父类test1");
    }
}
class Derived extends Base{
public void test1(){
    System.out.println("子类test1");
}
    public void test2(){
       this.test1();
       super.test1();
    }
}

(3)调用当前类中的其他构造方法,super调用父类的构造方法

当子类继承父类时,在实例化子类对象是,要先帮助父类进行构造(初始化父类成员),初始化父类成员,只有一种方法调用构造方法。

当给父类写构造方方法时,编译器会默认存在一个没有参数的构造方法,与此同时,子类里也会有一个没有参数的构造方法,且该构造方法中存在一个<super();>,为对父类构造方法的调用。

子类调用父类的构造方法时,用super(成员变量1,成员变量2...),该语句要写在子类构造方法的第一行,与this调用有参数的构造方法方法相似。

相同点:

1,均不能在静态方法中访问,因为this,和super都是指向对象的,而静态方法与对象无关

2,this(),和super()都要放到首行,故两者不能同时存在

3,两者都是关键字

总结:代码执行时,父类和子类的静态方法先执行>父类实例代码块与构造方法>子类实例代码块与构造方法

class Animal{
    public String name;
    public int age;
    public String color;

    public Animal(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;

    }//先执行父类的构造方法在执行子类的
    static {
        System.out.println("Animal::static");
    }
    {
        System.out.println("Animal::{}");
    }

    public void eat(){
        System.out.println(this.name+" is eating");
    }
}

class Dod extends Animal{
    public Dod (){
        super("dahuang",10,"黄色");
        //必须在第一行
    }
    static {
        System.out.println("Dod::static");
    }
    {
        System.out.println("Dod::{}");
    }
    public void bark(){
        System.out.println(this.name+"汪汪汪");
    }

}
public class Text {
    public static void main(String[] args) {
        Dod dog=new Dod();
        dog.eat();
        dog.bark();
    }
}

3,final关键字

1,可以用来修饰变量,成员方法以及类

2,修饰的变量或字段,表示为常量,不能被修改

3,修饰类,表示此类不能被继承,被称之为密封类

4,修饰方法,这种方法不能被重写

4,继承和组合

组合和继承都可以实现代码复用,应该使用继承还是组合,需要根据应用场景来选择,一般建议:能用组合尽量用组合。

class Tire{
}
class Engine{
}
class VehicleSystem{
}
class Car{
    private Tire tire; 
    private Engine engine; 
    private VehicleSystem vs; 
// ...
}
class Benz extends Car{
// 将汽车中包含的:轮胎、发送机、车载系统全部继承下来
}

三,多态

1,基本概念

通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状 态。

2,实现条件

1. 必须在继承体系下

2. 子类必须要对父类中方法进行重写

重写又被称为覆盖,复写:

1,方法名相同
2,方法的参数列表相同
3,方法的返回值相同
必须发生向上转型,且通过父类引用调用了父类与子类同名的方法重写

例如:

class Animal{
    public String name;
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat(){
        System.out.println(this.name +" is eating");
    }
}
class Dog extends Animal{
    public Dog(String name, int age) {
        super(name, age);
    }
    public void bark(){
        System.out.println(this.name+" 汪汪叫");
    }
    @Override
    public void eat(){
        System.out.println(this.name+"正在吃狗粮");
    }
}
class Bird extends Animal{
    public Bird(String name, int age) {
        super(name, age);
    }
    public void qiqi(){
        System.out.println(this.name+" 吱吱叫");
    }
    @Override
    public void eat() {
        System.out.println(this.name+"正在吃鸟粮");
    }
    public void fly(){
        System.out.println(this.name+"正在飞");
    }
}

@Override注解,修饰该方法,该方法是被重写的。作用:帮你对重写进行检查

注意:

1,静态方法不能被重写

2,被private修饰的不能被重写

3,被final修饰的不能被重写

4,重写时,子类的权限要大于父类的权限

5,重写的返回值可以是父子关系

public Animal eat(){
    System.out.println(this.name +" is eating");
    return null;
}
public Dog eat(){
    System.out.println(this.name+"正在吃狗粮");
return null;
}

3. 通过父类的引用调用重写的方法

public static void main1(String[] args) {
    Animal animal1 = new Dog("小黄",12);
    animal1.eat();

这里发生了向上转型,向上转型分为三个时机;

1,直接赋值型:

Animal animal1 = new Dog("小黄",12);

2, 方法传参型:

public static void func1(Animal animal1){
   
}
public static void main2(String[] args) {//发生向上转型的时机
    func1(new Dog("小黄",12));
}

3,返回值型:

public static Animal func2(){
    return new Dog("小黄",12);
}

综上所述:

public static void func(Animal animal1){
    animal1.eat();
}
public static void main3(String[] args) {
  func(new Dog("小黄",12));   
  func(new Bird("小小鸟",10));
}

当animal引用的对象不同,调用eat的表现行为不一样,这就称为多态

向上转型不能调用到子类特有的方法。

向下转型:将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的 方法,此时:将父类引用再还原为子类对象即可,即向下转换。

Animal animal1 = new Dog("小黄",12);
不能写成Dog dog=animal1(把大范围给到了一个小范围)
Dog dog=(Dog)animal1;所以要强制类型转换
dog.bark();

我们通常书写为:

if (animal1 instanceof Bird){
    Bird bird=(Bird) animal1;
    bird.fly();
}
else {
    System.out.println("animal1 instanceof Bird not");
}

animal1 instanceof Bird这一步是为了提高向下转型的安全性。如果表达式为真,则可以进行转换

3,多态的优缺点

优点:

1,降低代码的复杂度,避免大量使用if-else

2,可扩展能量强

例如:

class Shape{
    public void draw(){
        System.out.println("画一个图形");
    }
}
class Rect extends Shape{
    public void draw(){
        System.out.println("画一个矩形");
    }
}
class Cycle extends Shape{
    public void draw(){
        System.out.println("画一个圆圈");
    }
}
class Tri extends Shape{
    public void draw(){
        System.out.println("画一个三角形");
    }
}
public static void drawMaps(){
    Rect rect=new Rect();
    Cycle cycle=new Cycle();
    Tri tri=new Tri();
    Shape [] shapes={rect,rect,cycle,tri};
    for (Shape shape:shapes){
        shape.draw();
    }
}
public static void main(String[] args) {
    drawMaps();
}

缺点:代码运行效率低

  • 13
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值