初探面向对象--(Java版)

1.面向过程和面向对象

面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。

生活中洗衣服:
面向过程:需要自己写关于放衣服,开洗衣机,清洗衣服,烘干衣服的函数,注重洗衣服的整个过程,如果再增添一些其他的函数,对于整个洗衣服过程的开发和维护非常麻烦
面向对象:我们把洗衣服过程分为两个类(人和洗衣机),定义的人  类中有放衣服,开洗衣机等函数(方法),定义的洗衣机  类有清洗和烘干的函数(方法),把这两个类定义好后,通过类创建对象,让对象去执行以上的方法。相比于面向过程,面向对象编程,把事务先分解到对象身上,描述各个对象的作用,然后才是它们之间的交互。
或者这样理解:面向过程是编年体,面向对象是纪传体。

2.类

类是对实体对象的描述。其中有成员方法和成员属性

// 类的定义和实例化


class Student {
    // 成员变量/属性
    public int age;
    public String name;
    // 成员方法
    public void eat() {
        System.out.println(name +"正在吃饭");
    }
}
public class Main {
    public static void main(String[] args) {
        Student student1 = new Student();
        student1.name = "张三";
        student1.age = 10;
        System.out.println(student1.name);
        System.out.println(student1.age);
        student1.eat();

        Student student2 = new Student();
        student2.name = "李四";
        student2.age = 11;
        System.out.println(student2.name);
        System.out.println(student2.age);
        student2.eat();
    }
}

3.this

this 引用指向当前对象 (哪个对象的引用  调用了 该方法 this就调用哪个对象 )

this的一些用法:

1.通过this访问当前对象的   成员变量
2.通过this访问当前对象的   非静态成员方法
3.通过this访问当前对象的   其他构造方法(this在访问其他对象的构造方法时必须在第一行,不能形成环)

第一条
public class Date {
    public int year;
    public int month;
    public int day;

    public void setDate(int y,int m,int d) {
        year = y;
        month = m;
        day = d;
    }  // 这样子可以打印正确的日期
    public void setDate1(int year,int month,int day) {
        year = year;
        month = month;
        day = day;
    }  // 但是这样子是打印的0年0月0日,为什么呢?是形参自己给自己赋值
       // 没有修改到对象当中去,那我们怎样改
    public void setDate2(int year,int month,int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }  // 正确的打印
    public void printDate() {
        System.out.println(year+" 年"+month+" 月"+day+" 日");
    }
    public static void main(String[] args) {
        Date date = new Date();
        date.setDate2(2001,1,1);
        date.printDate();
    }
}

第二条

public class MyClass {
    private int number;

    public MyClass(int number) {
        this.number = number;
    }

    public void printNumber() {
        System.out.println("Number: " + this.number);
    }

    public void setNumber(int number) {
        this.number = number;
    }
}

4.构造方法

1.名字与类名相同,没有返回值类型,设置为void也不行
2.一般情况下使用public修饰
3.在创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次
4.不止一个构造方法,可以方式重载
用途:构造方法  作用就是用来初始化 对象中的成员
注:没有写构造方法时:Java帮助提供一个不带参数构造方法,但是有了其他的构造方法,Java就不再提供了

含有this的第三条:

public class Date {
    public int year;
    public int month;
    public int day;
    // 无参构造
    public Date() {
        this.year = 2000;
        this.month = 1;
        this.day = 1;    //   打印2000-1-1
        System.out.println("这是一个无参构造");
    }
    // this访问其他构造方法
     public Date() {
        this(2011,2,21);  //  打印2011-2-21
        System.out.println("这是一个无参构造");
    }
    // 带参构造
    public Date(int year,int month,int day) {
        this.year = year;
        this.month = month;
        this.day = day;
        System.out.println("这是带有三个参数的构造");
    }

    public void printDate() {
        System.out.println(year+"-"+month+"-"+day);
    }

    public static void main(String[] args) {
        Date date1 = new Date();  // 调用无参构造
        Date data2 = new Date(2022,3,3);  // 调用含有参数的构造
        date1.printDate();
        data2.printDate();
    }
}

5.封装

对外隐藏类的内部实现细节,只是对外提供了一些可以公开访问的内容

这其中涉及到访问修饰限定符(public,private,protected),要理解这些访问修饰限定符的作用,首先要了解包的概念

包是用来组织类和接口的一种机制,可以通俗的看成文件夹,文件夹里的文件就是相关的类和接口,包形成了一个独立的空间,可以避免命名冲突,而且可以很好的管理代码

导入包中的类:

// 方法一:直接导入包中的类
import java.util.Date(推荐)
但是不推荐使用  import java.util.*
例如:如果不同的包中存在相同名称的类,导入所有类可能会导致命名冲突。这会使得代码难以理解和维护,因为无法确定使用的是哪个类
// 方法二:
java.util.Date date = new java.util.Date();

先是创建了一个person包,之后在包中创建了一个Test01的类,在Test这个包中去引用Test01()这个类(导入自定义包)

浅谈封装

由于private将类给封装好,外界无法直接访问,需要通过给的接口进行访问,例如

class Student {
    private String name;
    private int age;
    private String stuNum;
    public static String className;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getStuNum() {
        return stuNum;
    }
    public void setStuNum(String stuNum) {
        this.stuNum = stuNum;
    }
    public void show() {
        System.out.println(" 姓名:"+this.name+" 年龄:"+this.age+" 学号:"
                +this.stuNum);
    }
}
public class Main {
    public static void main(String[] args) {
        Student student = new Student();
       // student.name = "haha";   err
       // System.out.println(student.name);    err
        student.setName("张三");
        student.setAge(20);
        student.setStuNum("001");
        System.out.println(student.getName());
        System.out.println(student.getAge());
        System.out.println(student.getStuNum());
    }
}

default表示前面没有修饰时的默认情况
现阶段不讨论子类的问题,#表示有此功能

包的访问控制权限
privatedefaultprotectedpublic
同一个包的同一个类####
同一个包的不同类###
不同包的子类##
不同包的非子类#

6.static成员

静态成员变量

被static修饰的成员,不属于对象,是属于静态的成员变量(类变量),或者说是属于类的,存在与方法区中,类加载时就会被初始化。
可以通过对象的引用来访问,也可以通过类名来进行访问,但是通过类名来访问更加的合理

public class Main {
    public static void main(String[] args) {
        Student student = null;
        Student.className = "1班";
        System.out.println(Student.className);
    }
}

以上正确打印

public class Main {
    public static void main(String[] args) {

        Student student = null;
        student.name = "李四";
        System.out.println(Student.name);
    }
}

先把name前面的private改为public,但由于name必须通过对象访问,因此有误

静态成员方法

1.静态成员方法当中 不能够直接调用非静态方法,因为静态方法不依赖与对象,可以直接通过类名访问,但是非静态成员方法依赖对象,需要通过对象的引用访问;例如

2.static 方法中不能使用this关键字

静态成员变量的初始化

1.构造方法初始化

class Student {
    public String name;
    public int age;
    public String stuNum;

    // 构造方法
    public Student(String name, int age, String stuNum) {
        this.name = name;
        this.age = age;
        this.stuNum = stuNum;

    public void show() {
        System.out.println("姓名:" + this.name + " 年龄:" + this.age + " 学号:" + this.stuNum);
    }

    public static void main(String[] args) {
        // 创建对象时调用构造方法进行初始化
        Student student = new Student("张三", 20, "20240001");
        student.show();
    }
}

2.get,set初始化--见浅谈封装

3.直接初始化

static int age = 5;

4.代码块初始化

要想知道什么是代码块初始化,首先了解什么是代码块

代码块

class stu{
    public String name;
    public int age;
        {
            this.name = "lihua";
            this.age = 22;
        System.out.println("2.实例代码块");
    }
    public stu(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("3.构造方法(代码块)");
    }
    static {
        System.out.println("1.静态代码块");
    }
}
public class The {
    public static void main(String[] args) {
        stu s = new stu("王五",5);
        System.out.println("==============");
        stu s1 = new stu("王五",5);
    }
}

那么它们之间的执行顺序是什么,按照绝对顺序:静态代码块,实例代码块,构造方法,而且静态的只执行一次,这个类只加载一次,如果都是实例代码块,和定义顺序有关

6.对象的打印

public class The {
    String name;
    String gender;
    int age;
    public The(String name, String gender, int age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }
    public static void main(String[] args) {
        The person = new The("Jim","男", 18);
        System.out.println(person);
    }
}

实际上是打印了该对象的内存地址而不是对象的内容。这是因为在默认情况 System.out.println()方法会调用对象的toString() 方法来获取对象的字符串表示形式,类中的toString()方法会返回对象的内存地址。那应该怎样修改,加上即可

@Override
    public String toString() {
        return "The{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                '}';
    }

由于println是一个方法,我们需要看它的实现

当我们实现自己的toString时,是会调用我们自己的,如果自己没有实现,则会调用官方的

7.继承

如何继承

在现实世界中,我们常常会发现一些事物的共性,例如狗和🐟都是动物,如果我们想要描述它们的一些行为或者其他特性。如果采用这样的代码(如下)

public class Dog {
    public int age = 3;
    public String name = "旺财";
    public void eat() {
        System.out.println(this.name+"正在吃食物");
    }
    public void bark() {
        System.out.println(this.name+"正在汪汪叫");
    }
}

public class Fish {
    public int age = 1;
    public String name = "小金鱼";
    public void eat ()  {
        System.out.println(this.name+"正在吃食物");
    }
    public void swim() {
        System.out.println(this.name+"正在水中游");
    }
}

显得代码冗杂,我们发现,其中有些共同的特征,我们把这些共同的特征抽离出来,实现代码的复用,因此我们可以修改(需要借助extends关键字)为(如下):我们先定义一个动物类,表示🐟和🐕的父类,我们把🐟和🐕称之为子类

public class Dog extends Animal{
    public void bark() {
        System.out.println(this.name+"正在汪汪叫");
    }
}

public class Fish extends Animal{
    public void swim() {
        System.out.println(this.name+"正在水中游");
    }
}

public class Animal {
    public int age;
    public String name;
    public void eat() {
        System.out.println(this.name+"正在吃食物");
    }
}

那么子类和父类都有一个相同的成员变量,访问哪一个呢(打印出的是20 2 10,为什么呢)

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

public class Derived extends Base{
    public int c = 10;
    public int a = 20;
    public void func() {
        System.out.println(this.a);
        System.out.println(this.b);
        System.out.println(this.c);
    }
}

上述情况访问子类的成员变量,那么如何访问父类的成员呢,加上super就可以了

我们可以这样子理解super和this的关系

那么对于成员方法呢,对的,也是一样优先访问子类,如果是构成了重载(例如fun1),根据调用 方法适传递的参数选择合适的方法访问

//  子类
public void fun1() {
        System.out.println("the Base fun1");
    }
    public void fun2() {
        System.out.println("the Base fun2");
    }

// 父类和结果
public void fun1(int a) {
        System.out.println("the Derived fun1 + int");
    }
    public void fun2() {
        System.out.println("the Derived fun2");
    }
    public void fun3() {
        fun1();  // 无参  the Base fun1
        fun1(2);  // 传参 -- the Derived fun1 + int
        fun2();  //  the Derived fun2
        super.fun2();  // the Base fun2
    }

总结下来一句话:就近原则

super关键字

1.只能在类的非静态方法中使用,用来访问非静态成员方法和字段(由于静态的不依赖于对象,而用super指代的依赖于对象)
2.只能指代当前的父类,不能指代父类的父类
3.调用父类构造方法

注:super和this必须在方法的第一行,否则会报错,也隐含了super和this不能共存

如何理解呢,当子类 继承 父类之后,子类需要显示调用父类的构造方法,帮助父类成员进行初始化,那么如何进行初始化?

如果没有提供任何构造方法,那么Java 会提供,如果自己写了一个,则不会提供

一些执行顺序

按图说话了--看最后的结果

下面我们把dog执行两次

注:java不支持多继承,但可以通过接口的方式支持

接口下篇博客写,点个关注吧(doge)

protect关键字

我们知道:private只能在当前类中使用,default只能在当前包中使用,public可以在任何地方使用,那么protect可以在当前包中使用又可以在不同包中的子类使用是什么意思呢?如图

final关键字

1.如果不想被继承,使用final关键字   --  还是拿动物,🐕,🐟举例子,由上面可知,🐟和🐕继承了动物,那么我们加上final关键字就会
2.将变量修饰为常量

8.多态

我们应该如何理解多态,通俗来讲就是完成某个行为,不同的对象会产生不同的状态

在认识多态前,我们首先要了解重写,向上转型和向下转型等概念

向上转型  --  父类引用了子类对象

以下代码为了说明转型

package demo2;

public class Animal {
    public int age;
    public String name;
    public void eat() {
        System.out.println(this.name+"正在吃食物");
    }
    public Animal(String name,int age) {
        this.name = name;
        this.age = age;
    }
    public String toString() {
        return "名字: " + name + ", 年龄: " + age;
    }
}

package demo2;
public class Dog extends Animal {
    public Dog(String name,int age){
        super(name,age);
    }
    public void bark() {
        System.out.println(this.name+"正在汪汪叫");
    }
}

package demo2;

public class Fish extends Animal {
    public Fish(String name,int age) {
        super(name,age);
    }
    public void swim() {
        System.out.println(this.name+"正在水中游");
    }
}

方法1:直接赋值  

方法2:方法传参

方法3:返回值

缺点:

重写和重载

重写需要满足什么条件?

1.方法名字一样
2.参数列表相同
3.返回值相同

那么我们看两组代码(这里包含向上转型和动态绑定),我们调用的是animal的eat方法。为什么打印   旺财正在吃骨头

public static void main(String[] args) {
        Animal animal = new Dog("旺财",2);
        animal.eat();
    }

------------测试类--------------------------
public void eat() {
        System.out.println(this.name+"正在吃食物");
    }

-------------父类Animal---------------------
public void eat() {
        System.out.println(this.name+"正在吃骨头");
    }

-------------子类Dog-----------------------
父类和子类的方法构成了重写

一般在重写的方法前加上@Override,只是一个标注,如果不是重写则会报错

由此我们可以得到:程序在编译的时候,确实调用的是父类的eat,当代码运行时,通过父类的引用,调用了父类和子类重写的哪个方法,结果实际调用了子类的方法,此时  我们把这个情况叫做动态绑定

那么可以动态绑定的条件是什么?

1.父类引用  引用 子类对象
2.通过父类的引用,调用了父类

注意事项:

1.不能是一个静态方法


2.被finnel修饰不能被重写


3.如果子类重写父类方法  子类权限要大于等于父类的权限

4.被private修饰的方法不能被重写

5.构成父子类关系也是重写

重写和重载的区别

1.重写是针对继承关系,重载是在同一个类针对方法参数不同给予的多种实现
2.重写是覆盖父类中的方法,满足以上条件(见上--重写与重载开始)+返回类型可以是子类类型。它是对于父类改写满足子类需求。
3.重载是方法名相同,参数列表不同,返回值可以相同也可以不同。它是为了适应不同参数组合。
而且它是静态方法的代表。
静态方法:在编译时,根据用户的传参调用的方法。例如:Add1(int a ,int b);  Add 2(int a,int b,intc)我们计算加法时,如果是两数相加调用Add1,三个数调用Add2

向下转型 -- 强转

但是不都是可以成功的 :上面是引用的Fish用Fish强转,但下面引用的是Dog用Fish强转

我们可以这样修改

多态

引用的对象不用样,但是调用的同一个方法,表现出的现象不一样,这种思想叫做多态

public class Shape {
    public void draw(){
        System.out.println("画一个图形");
    }
}

public class Round extends Shape{
    @Override
    public void draw(){
        System.out.println("画一个圆");
    }
}

public class Rectangle extends Shape{
    @Override
    public void draw(){
        System.out.println("画一个矩形");
    }
}

public class Square extends Shape{
    @Override
    public void draw(){
        System.out.println("画一个正方形");
    }
}

public class test {
    public static void drawMap(Shape shape) {
        shape.draw();
    }
    public static void main(String[] args) {
        Round round = new Round();
        Rectangle rectangle = new Rectangle();
        Square square = new Square();
        drawMap(round);
        drawMap(rectangle);
        drawMap(square);
    }
}
或者
public static void main(String[] args) {
        Round round = new Round();
        Rectangle rectangle = new Rectangle();
        Square square = new Square();
        Shape[] shape = {rectangle,round,square};
        for(Shape shape1 :shape) {
            shape1.draw();
        }
    }
结果---------------------------------------
画一个圆
画一个矩形
画一个正方形

这就是多态

如果不使用多态则会使用大量的if-else(如下)
优点:可扩展能力强

public static void main(String[] args) {
        Round round = new Round();
        Rectangle rectangle = new Rectangle();
        Square square = new Square();
        String[] strings = {"round","rectangle","square","square","rectangle"};
        for(String s:strings){
            if(s.equals("round")) {
                round.draw();
            }else if(s.equals("square")) {
                square.draw();
            }else {
                rectangle.draw();
            }
        }
    }

// 结果-----------------------------------------------
画一个圆
画一个矩形
画一个正方形
画一个正方形
画一个矩形

缺点:代码的运行效率降低

1.属性没有多态性:当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性。
2.构造方法没有多态性

一个有坑的代码

class B {
    public B() {
        func();
    }
    public void func() {
        System.out.println("B.func()");
    }
}
class D extends B {
    private int num = 1;
    @Override
    public void func() {
        System.out.println("D.func() " + num);
    }
}
public class test {
    public static void main(String[] args) {
        D d = new D();
    }
}

运行结果
D.func() 0

完成收工,点个关注把^o^....

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值