Java面向对象编程详解

目录

 

面向对象编程一览

1.如何创建包呢?

2.如何导入包

3.包的命名

4.常用的包

5.包访问权限(非常重要)

封装

什么是封装呢?

封装的好处

封装的实现步骤

继承

继承使用背景

extends关键字

继承图解

final关键字

super关键字

多态

引一个例子:

多态的概念

具体体现

向上转型

向下转型

动态绑定

抽象类

概念介绍

使用细节

abstract关键字

接口

概念

定义接口

extends与implements的区别

接口与抽象类的区别


面向对象编程一览

 

 

接下来,我们来对于面向对象的每一个分支做一个详细的知识点剖析。

面向对象与面向过程区别例子

举个例子:洗衣服

面向过程:关注点是过程 ————站在一个执行者的角度去考虑事情,做事情

  1. //step1.找个盆

  2. //step2.收集要洗的衣服

  3. //step3.放水,放洗衣粉。。

  4. //step4:洗一洗

  5. //step5:晒一晒   

​面向对象:关注点是对象 ————站在指挥者的角度

//step1:找个对象

//step2:让他洗衣服

包是组织类的一种方式,使用包的目的是保证类的唯一性!

举个例子:如果你与你的小伙伴or同事正在写同一个项目,你写了一个类叫TestDemo,你的小伙伴or同事也写了一个类叫TestDemo,这样会导致类名冲突,故为了防止类名冲突,我们引用包这个概念,能够很好的防止类名的冲突。

1.如何创建包呢?

以IDEA为例

点击src -> 右键->New->Package

 

得到下图,接着我们只需要写包名就好了。

以上也可以说是把类放到包中

2.如何导入包

使用import关键字

我们来看一个简单的代码

import java.util.Scanner;//import关键字导入 java.util这包下的 Scanner类!
public class TD {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        System.out.println(n);
    }
}

import关键字导入 java.util这包下的 Scanner类!

如果想要使用其他的类,可以采用以下方式:

import java.util.*;
public class TD {
    public static void main(String[] args) {
        //以下代码用了java.util包中的Scanner和Arrays这两个类!
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        System.out.println(n);
        int[] array ={45,12,4,5,3};
        Arrays.sort(array);
        for(int x:array){
            System.out.print(x+" ");
        }
    }
}

以上代码用了java.util包中的Scanner和Arrays这两个类!

所以我们会发现*很好用,但我更建议使用显示调用包中的类,否则还是容易出现类名冲突的情况!

3.包的命名

命名规则:

只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或者保留字!

命名规范:

一般是小写字母+小圆点

eg:

com.公司名.项目名.业务规模名

com.xxx.xxxx.user //用户模块

4.常用的包

常用的包:

一个包下,包含很多常用的类!

java常用的包有:

java.lang.*  //lang包是基本包,默认引入,不需要再引入

java.util.*  //util包,系统提供的工具包,工具类,使用Scanner还有Arrays,集合等!

java.net.* // 网络包 网络开发

java.awt.* //界面开发

java.sql.* //进行数据库开发的支持包

java.io.* // I/O编程开发包

5.包访问权限(非常重要)

Java中提供四种访问控制修饰符号控制方法和属性(成员变量)的访问权限(范围):

1.public: 公开级别,对外公开,是最大的访问权限修饰符,其修饰的成员变量、构造方法和普通方法可在任何一个类中被操作或使用;

2.protected:受保护级别,用protected修饰时,对子类和同一个包中的类公开;

3.default: 默认级别(也叫包访问权限),其修饰的成员变量、构造方法和普通方法可以在其定义类中和与定义类同包的其它类(可以使子类)中使用;

4.private:私有级别,只有类本身可以访问,不对外公开,是最小的访问权限控制符;

对类来说,只有默认和public才能修饰类!

package Test;

public class TestD {
    int a = 10;//什么访问修饰符都没有写,默认就是一个包访问权限
    
}

当前这个a,只能在和当前类TestD同一个包底下的类里面才能使用。

封装

什么是封装呢?

封装:就是把抽象的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(方法),才能对数据操作

封装的好处

1.隐藏实现细节。eg:方法的调用,比如Arrays中的sort函数,直接调用就好。

2.可以对数据进行验证,保证安全合理。

3.只能通过规定的方法访问数据。

封装的实现步骤

1.将属性进行私有化private,[不能直接修改属性]!!

2.提供一个公共的set方法,用于对属性判断并赋值,如下。

3.提供一个公共的get方法,用于获取属性的值。

public void setXXX(类型 参数名){
    //加入数据验证的业务逻辑
    属性 = 参数名;
}
public 数据类型 getXXX(){//权限判断,XXX是某个属性
    return xx;
}

继承

继承使用背景

两个类的属性和方法有很多是相同的,怎么办?

代码中创建的类,主要是为了抽象现实中的一些事物(包含属性和方法),有的时候客观事物之间就存在一些联系,那么在表示成类和对象的时候也会存在一定的关联。

用代码举例:我们来定义三个类一个是动物类,一个是猫类,一个是鸟类。

class Animal {
    public String name;

    //有参构造器,用来初始化实例
    public Animal(String name){
        this.name = name;
    }
    public void eat(String food){
        System.out.println(this.name + "正在吃"+food);
    }
}
class Cat {
    public String name;

    public Cat(String name){
        this.name = name;
    }

    public void eat(String food){
        System.out.println(this.name + "正在吃"+food);
    }
}
class Bird {
    public String name;
    
    public Bird (String name){
        this.name = name;
    }
    
    public void eat(String food){
        System.out.println(this.name + "正在吃"+food);
    }
    public void fly(){
        System.out.println(this.name + "正在飞");
    }
}

我们发现,这个代码其中很多部分重复!

仔细分析,我们发现Animal,Cat和 Bird这几个类存在一定的关系:

1.这三个类都有一个相同的属性 name,且实际意义是完全一样

2.这三个类都具备一个相同的方法 eat()方法,而且行为一样。

3.从逻辑上将,Cat 和Bird都是Animal 这体现了一种(is-a关系

此时我们就可以让Cat和Bird分别继承Animal类,达到代码重用的效果!

extends关键字

语法规则:

class 子类类名 extends 父类类名{

}

1.使用extends指定父类

2.Java不像C++或Python一样支持多继承,只存在单继承。

3.子类会继承父类的所有public 方法和字段。

4.对于父类中的private的字段和方法,子类中是无法访问的。

5.子类的实例中,也包含着父类的实例,可以使用super关键字得到父类实例的引用。

对于上述代码,可以通过继承进行改进。

class Animal {
    public String name;

    //有参构造器,用来初始化实例
    public Animal(String name){
        this.name = name;
    }
    public void eat(String food){
        System.out.println(this.name + "正在吃"+food);
    }
}
class Cat extends Animal{
    public Cat(String name){
        //使用super调用父类构造方法
        super(name);
    }
}
class Bird extends Animal{
    public Bird(String name){
        super(name);
    }
    public void fly(){
        System.out.println(this.name + "正在飞");
    }
}

注意:如果继承过程中,父类中有构造方法,则子类中一定要实现该构造方法,并使用super关键字来显示调用,否则会报错!

继承图解

我们可以得出一个结论,父类的方法和属性数是不大于子类的方法和属性数的

final关键字

final关键字我们都很熟悉,它的功能之一是修饰一个变量或字段的时候,表示成为常量,使之不能修改。

例如:

final int a = 10;
a = 20;//编译报错

final关键字也能修饰类,此时表示被修饰的类不能被继承,起限制作用。

final class A{
}
class B extends A{
}

如下图,放到编译器中会出现警告,以IDEA为例,会显示不能继承final修饰的类。

super关键字

基本概念:super可以用来访问父类的构造方法,普通方法和属性,即是父类对象的引用

super关键字的功能:

  • 在子类的构造方法中显式的调用父类构造方法
  • 访问父类的成员方法和变量。

super关键字有三种用途

1.super.成员变量  -> 访问父类的属性,但不能访问父类的private属性

2.super.成员方法 -> 访问父类的方法,不能访问父类的private方法

3.super(参数列表) - >访问父类的构造器,注意这句话只能放在构造器的第一句,且只能出现一句!

class A{
    public int a = 10;
    public int b = 10;

    public A(int a,int b){
        this.a = a;
        this.b = b;
    }
}
class B extends A{
    public int c;
    public B(int a,int b,int c){
        super(a,b);//调用父类的构造器,初始化a,b;
        this.c = c;
    }
}

多态

引一个例子:

主人汤姆给大黄狗吃排骨,给小花猫吃黄花鱼。

我们本能想法就是 方法的重载来实现这个过程。

代码如下:

public class Master {
    private String name;
    public Master(String name){
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public void feed(Dog dog,Bone bone){
        System.out.println("主人 "+name+"给 " +dog.getName()+"吃 "+bone.getBones());
    }
    //方法的重载
    public void feed(Cat cat,Fish fish){
        System.out.println("主人 "+name+"给 " +cat.getName()+"吃 "+fish.getFish());
    }
}

class Bone{
    private String bones;

    public Bone(String bones) {
        this.bones = bones;
    }

    public String getBones() {
        return bones;
    }

    public void setBones(String bones) {
        this.bones = bones;
    }
}
class Fish{
    private String fish;

    public Fish(String fish) {
        this.fish = fish;
    }

    public String getFish() {
        return fish;
    }

    public void setFish(String fish) {
        this.fish = fish;
    }
}
class Animal{
    private String name;

    public Animal(String name){
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
class Cat extends Animal{
    public Cat(String name){
        super(name);
    }
}
class Dog extends Animal{
    public Dog(String name){
        super(name);
    }
}

public class Test {
    public static void main(String[] args) {
        Master tom = new Master("汤姆");
        Dog dog = new Dog("大黄");
        Bone bone = new Bone("排骨");

        tom.feed(dog,bone);

        Cat cat = new Cat("小花猫");
        Fish fish = new Fish("黄花鱼");

        tom.feed(cat,fish);
    }
}

我们的运行结果如下

虽然解决了问题,但是我们想一下,如果我们呢今后拓展更多的动物种类和更多的食物种类,那么feed方法会非常多,且不易于管理。

故我们引出多态这个概念。可以提高代码的复用性,易于代码维护。

多态的概念

多态是指方法或者对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

具体体现

1.方法的多态

1.重载的方法一定体现多态。

2.重写的方法一定体现多态。

public class PolyMethod {
    public static void main(String[] args) {
        //方法重载体现多态
        B b = new B();
        //我们传入不同的参数会调用不同的sum方法,体现了多态
        System.out.println(b.sum(10,20));
        System.out.println(b.sum(10,20,30));
        A a = new A();
        //方法的重写(覆盖)
        b.print();
        a.print();
    }
}
//父类
class A{
    public void print(){
        System.out.println("A print() 方法被调用....");
    }
}
//子类
class B extends A{
    public int sum (int n1,int n2){
        return n1+n2;
    }
    //和上面sum构成重载
    public int sum (int n1,int n2,int n3){
        return n1+n2+n3;
    }
    public void print(){
        System.out.println("B print() 方法被调用....");
    }
}

运行结果如下:

2.对象的多态

1.一个对象的编译类型和运行类型可以不一致。

2.编译类型在定义对象时,就确定了,不能改变!

3.运行类型可以是变化的。

4.编译类型看定义时 =号左边的,运行类型看 =号右边的

我们来看一个例子

class Animal{
    public void say() {
        System.out.println("Animal say()动物在叫");
    }
}

class Cat extends Animal{

    public void say(){
        System.out.println("Cat say() 小猫喵喵叫");
    }
}
class Dog extends Animal{
    public void say(){
        System.out.println("Dog say() 小狗汪汪叫");
    }
}
public class Test {
    public static void main(String[] args) {
        /**
         *animal的编译类型是Animal 运行类型是Dog
         *调用方法时看运行类型,因为animal的运行类型是Dog所以say()方法执行Dog的say()方法
         */
        Animal animal = new Dog();
        animal.say();
        System.out.println("=================");
        /**
         * animal的编译类型是Animal 运行类型是Cat
         * 调用方法时看运行类型,因为animal的运行类型是Cat所以say()方法执行Cat的say()方法
         */
        animal = new Cat();
        animal.say();

    }
}

运行结果如下:

多态的前提是两个对象(类)存在继承关系!

向上转型

1.本质:父类的引用 引用了子类的对象

2.格式 :   父类类型  引用名 = new 之类类型()

3.特点:a.编译类型看左边,运行类型看右边。

              b.可以调用父类中的所有成员(需要遵守访问权限),不能调用子类中特有成员。

              c.最终运行效果看子类的具体实现!

4.发生向上转型的时机:

              a.直接赋值 ->父类引用 引用了子类的对象 

              b.函数的传参  ->函数的参数中包含了父类的引用

              c.函数的返回值  -> 函数返回值是父类

public class TS {
    public static void func(Animal animal){

    }
    //方法的返回值
    public static Animal func1(){
        Cat cat = new Cat();
        return cat;
    }
    public static void main(String[] args) {
        //父类的引用  引用了  子类的对象
        Animal animal = new Cat();

        Cat cat1 = new Cat();
        Animal animal1 = cat1;

        //函数的传参
        func(cat1);

        //方法的返回值
        func1();
    }
}

向下转型

1.语法: 子类类型 引用名 = (子类类型)父类引用;

2.注意事项:a.只能强转父类的引用不能强转父类的对象

                     b.要求父类的引用必须指向的是当前目标类型的对象

                     c. 当向下转型后,可以调用子类类型中的所有成员

Animal animal1 = new Animal();
//向下转型
//cat的编译类型是Cat 运行类型是Cat
Cat cat = (Cat) animal1;

动态绑定

我们来举一个例子:

class A{
    public int i = 10;

    public int sum(){
        return getI()+10;
    }
    public int sum1(){
        return i+10;
    }
    public int getI() {
        return i;
    }
}
class B extends A{
    public int i = 20;
    public int sum(){
        return i+20;
    }
    public int getI(){
        return i;
    }
    public int sum1(){
        return i+10;
    }
}
public class DynamicBinding {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.sum());
        System.out.println(a.sum1());
    }
}

答案是 40 30!

java的动态绑定机制:

1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定。

2.当调用对象属性的时候,没有动态绑定的机制,哪里声明,哪里使用。

抽象类

概念介绍

1.用abstract关键字来修饰一个类时,这个类就叫抽象类。

语法格式

访问修饰符 abstract 类名{

}

2.用abstract关键字来修饰一个方法时,这个方法就是抽象方法。

语法格式

访问修饰符 abstract 返回类型 方法名(参数列表);//没有方法体

3.抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类

4.在框架和设计模式中使用的比较多。

使用细节

1.抽象类不能实例化对象。

2.抽象类不一定要包含abstract方法,也就是说,抽象类可以没有abstract方法,还可以有实现的方法。

abstract class A{
    public void hi(){
        System.out.println("hi");
    }
}
public class abst {
    public static void main(String[] args) {
        new A();
    }
}

3.一旦类包含了abstract方法,则这个类必须声明为abstract

class B{
    public abstract void hi(){
    }
}

4.抽象类也是一个类,可以有任意成员,比如非抽象方法,构造器等。

5.抽象方法不能有主体,即不能实现。

abstract class C{
    public int n = 10;
    public String name;

    public void hi(){
        System.out.println("hi");
    }
    public C(){
    }
    public C(String name,int n){
        this.name = name;
        this.n = n;
    }
}

6.如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为抽象类。

abstract关键字

abstract关键字只能修饰类和方法,不能修饰属性和其他。

如果用abstract修饰的抽象方法,是不能用private,final和static来修饰的,因为这些关键字都是与重写相违背的。

接口

概念

接口是抽象类的更进一步,抽象类中还可以包括非抽象方法和字段,而接口包含的方法都是抽象方法,字段只能包含静态常量

定义接口

interface关键字表示声明,也叫定义一个接口。

语法规则

interface 接口名{

抽象方法;

}

注:

1.接口中的方法一定是抽象方法,因此可以省略abstract

2.接口中的方法一定是public,因此可以省略public

3.在调用的时候同样可以创建一个接口的引用,对应到一个子类的实例

4.接口不能单独被实例化。

extends与implements的区别

extends表示当前已经具有一定功能,进一步扩充功能。

implements表示当前啥也没有,需要从头构造。

接口与抽象类的区别

核心区别:抽象类中可以包含普通方法和普通字段,这样的普通方法和字段可以被子类直接使用(不必重写),而接口中不能包含普通方法,子类必须重写所有的抽象方法。

谢谢观看。

评论 12
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值