java第五话:类和对象详解
发布日期:2021-05-04 20:08:17 浏览次数:20 分类:技术文章

本文共 11597 字,大约阅读时间需要 38 分钟。

从类和对象开始了解面向对象编程

类和对象

类和对象的初步认知

什么是类

面向对象编程和面向过程编程

类和对象是java里面常常提到的东西,但对于初入门的小白来说这个概念确实挺让人模糊的。那么接下来就让我们来了解一下啥是类和对象吧!

在了解类和对象之前我们先对我们的C语言进行一下思考,我们在C语言中的编程思想是啥?假如我们将一件事情分成几个步骤,那么我们在C语言中是不是会将每个步骤分成一个或者几个函数来编写,然后一步一步的完成整件事情。这样的编程思想就叫面向过程编程
那么java中与之不一样的是,java是面向对象编程,简而言之,我们是针对某一个对象来编程的,而不是针对某一个事件。
具体请看如下代码:

class Person{       String name;    int age;    String sex;    public void eat(){           System.out.println(name + "吃了");    }}

(对了,class是声明一个类的关键字)

在上面代码中,我声明了一个 Person 类,类里面有存储名字的字符串 name,有存储年龄的整形 age,有表示性别的字符串 sex,还有每个人都会做的行为 eat() 。啥叫面向对象编程?这就是面向对象编程,我是针对每一个Person来进行编程。而不是针对某一个行为来进行编程

类的含义

如下图代码,我们写的Person类,相当于为所有人写了一个模板,毕竟人人都有名字,年龄,性别,都要吃东西。这就是类的含义,类,就是一个模板,就是一张图纸,就是提前刻画好的模具。但是有个问题,虽然我们都是人,都有名字,但是我和你的名字并不一样,那怎么办呢?这就要说到类的实例化和对象了。

class Person{       String name;    int age;    String sex;    public void eat(){           System.out.println(name + "吃了");    }}

类的实例化和引用和对象

上文我们说到类是一个模板,涉及的更具体的话就要了解类的实例化和对象。

话不多说,我们直接上代码 (要注意,new 是类实例化的关键字)

Person person1 =new Person();        Person person2 =new Person();        Person person3 =new Person();

如上图代码,我们就进行了类的实例化,实例化的结果是产生了三个引用,三个引用分别指向三个对象

若想更具体的从底层来了解是如何实例化的我们可以看下图
在这里插入图片描述
在我们实例化的时候,我们会在栈区产生如person1,person2这样的引用(变量),里面存储的是地址,指向堆区中的对象(真正用来存储如name,age这样变量的地方),如eat这样的方法,会在堆区的对象里以方法表的形式存储起来(也是存储地址,这个后面再谈)。到这
通过代码和图的讲解我相信你应该了解了啥是类的实例化,啥是引用,啥是对象。
总结一下:实例化是一个过程,引用在栈区中,存储的是对象的地址,对象在堆区中,存储的是类里面具体的成员变量和方法的地址

类的成员

了解了类的概念和类的实例化以后,我们来了解一下类是如何组成的。

class Person{       String name;//称为属性或者成员变量或者字段    int age;    String sex;    public void eat(){   //成员方法        System.out.println(name + "吃了");    }}

类的基本属性是由字段和方法构成的(当然还有构造方法,这个我们下文再说),通过 ’ . ’ 这个操作符访问到类的字段和方法

类的成员变量,你可以选择赋初值,也可以不赋值。如果你不赋值的话系统就会将其赋值为对应的默认的零值。具体是那些零值请看下图。
在这里插入图片描述

类的封装

认识private和public关键字

被public修饰的成员变量和成员方法,可以被类的调用者通过’ . ‘操作符进行访问。

被private修饰的成员变量和成员方法,不可以被类的调用者通过’ . '操作符进行访问。
private修饰的成员变量只能在当前类中被调用,在类外就不可以
如下图代码

class Person{       public String name;//可以被直接调用    private int age;//不可以直接被调用    private String sex;    public void eat(){           System.out.println(name + "吃了");    }}

那为什么要有private进行封装这样的操作呢?

我们就拿上面的类来举例子
请看如下代码

Person person1 =new Person();        Person person2 =new Person();        Person person3 =new Person();        person1.name = "张三";        person2.name = "李四";        person3.name = "王五";

在这段代码中,我调用了Person这个对象,同时,对对象里面的name这个字段进行了赋值,试想一下,如果将来我作为类的创建者,我将name这个字段这个变量修改为myname了,那么其他这些类的调用者,如上图代码还能使用吗?

很明显是不能的。因为,为了避免上述这种尴尬情况,同时降低类的调用者的使用成本,即调用者不用了解有些什么成员变量,只需要知道如何使用方法即可,我们就需要对成员属性使用private进行封装

学会setter和getter方法

我们在上面说可以用private对成员变量(成员方法也行)进行封装,那么我们如何访问和使用到已经被封装的成员变量呢?

我们就可以写关于每个成员变量的getter和setter,即对应的赋值和获取方法,具体如下图:

class Person{       public String name;    private int age;    private String sex;    public void eat(){           System.out.println(name + "吃了");    }    public int getAge() {           return age;    }    public void setAge(int age) {           this.age = age;    }    public String getSex() {           return sex;    }    public void setSex(String sex) {           this.sex = sex;    }}

另外,在IDEA中我们可以让编译器自动的生成getter和setter代码,具体步骤如下图

1.先找到图中的Generate选项
在这里插入图片描述
2.根据你的需要选择生成getter或者setter
在这里插入图片描述
这样,你就能让IDEA帮你自动生成封装的属性对应的getter和setter方法了。
在这里插入图片描述

类的构造方法(重要)

什么是构造方法

上面我们说到类是由成员变量和成员方法构成的,除此之外,类里面还有一个很重要的东西,就是构造方法。

所谓的构造方法,就是在使用 new 关键字实例化新对象是会被自动调用,用于对对象完成初始化操作的方法。
到这我们可以总结出一点,使用new关键字会有两步操作:
1.为对象在堆区分配内存空间 2. 调用类的构造方法

构造方法的规则是:

1.方法名必须和类名相同
2.构造方法没有返回类型的声明
3.一个类中至少存在一个构造方法,如果类中没有写,系统会默认帮你生成一个无参的构造方法(注:如果你写了构造方法,系统将不会帮你生成无参的构造方法)
4.构造方法支持方法重载,规则和正常方法重载的一致

下面用代码来具体表示一下构造方法

下图中,没有写构造方法,但是系统会默认有一个不带参,啥也不做的构造方法。

class Person{       public String name;    private int age;    private String sex;    public void eat(){           System.out.println(name + "吃了");    }}

下图中写了一个带参数的构造方法,但是系统将不会帮你默认生成一个不带参的构造方法。

class Person{       public String name;    private int age;    private String sex;    //这里的this关键字我们下文会讲    public Person(String name,int age,String sex){           this.name=name;        this.age=age;        this.sex=sex;    }    //因为age和sex都是private的,因此要写或者生成对应的getter方法才能获得    public int getAge() {           return age;    }    public String getSex() {           return sex;    }    public void eat(){           System.out.println(name + "吃了");    }}public class Test {       public static void main(String[] args) {           Person person1 =new Person("张三",18,"男");//这就是构造方法带参的使用,不带参就是里面啥也不写,new就完了。        Person person2 =new Person("李四",19,"男");        System.out.println(person1.name +" " +person1.getAge() +"岁 性别 "+person1.getSex());        System.out.println(person2.name +" " +person2.getAge() +"岁 性别 "+person2.getSex());    }    //输出结果    //张三 18岁 性别 男    //李四 19岁 性别 男

认识this关键字

this 在英文里是这,这个的意思,那么在java里面 this 关键字代表了当前对象的引用,因此,它才能有 this.属性 ,this.方法 , this() 这些操作。

this.属性

就拿上面的代码举例,这里面的this.name (即当前对象的引用.name), 就代表了这个类上面定义的成员变量,而后面的name就仅仅是参数里面传进来的局部变量,因此下图这样的代码,就能完成对成员变量的初始化。假如你不用this的话,你就单纯的写一个name=name,编译器并不知道你的name是成员变量还是局部变量。

public Person(String name,int age,String sex){           this.name=name;        this.age=age;        this.sex=sex;

this.方法

this.方法和this.属性差不多,都是使用this来访问类里面的成员变量或者成员方法。

下面给出一个代码示例

class Person{       public String name;    private int age;    private String sex;    public Person(String name,int age,String sex){           this.name=name;        this.age=age;        this.sex=sex;    }    public int getAge() {           return age;    }    public String getSex() {           return sex;    }    public void eat(){           System.out.println(name + "吃了");    }    public void SelfIntroduce(){           System.out.println("我的名字叫"+ this.name + " 我的年龄是 "+this.getAge()+" 我的性别是 "+this.getSex());    }}public class Test {       public static void main(String[] args) {           Person person1 =new Person("张三",18,"男");        Person person2 =new Person("李四",19,"男");        person1.SelfIntroduce();        person2.SelfIntroduce();    }    //输出结果:    //我的名字叫张三 我的年龄是 18 我的性别是 男    //我的名字叫李四 我的年龄是 19 我的性别是 男

this()

我们着重要讲一下的是this(),this() 是用来调用构造函数,下面举个例子

class Person{       public String name;    private int age;    private String sex;    public Person(){           this("王五",20,"男");//必须在第一行写    }    public Person(String name,int age,String sex){           this.name=name;        this.age=age;        this.sex=sex;    } }

上面中两个构造方法是重载的关系,第一个构造方法就通过this关键字调用了第二个构造方法。

认识static静态的含义

static 在C语言中也是静态关键字,那么我们先来回顾一下C语言里面static关键字有什么作用,然后再来对比着学习java里面的static关键字。

C语言中的 static 关键字
1.static 修饰局部变量,作用:static修饰的局部变量会被保存到静态区中,不是栈区,会延长局部变量的生命周期,即出了作用域,变量仍然存在。
2.static 修饰全局变量,作用: static 修饰的全局变量只能在本源文件中使用。
3.static 修饰函数,作用:static 修饰的函数只能在本源文件中使用。

复习了C里面的static作用,我们就可以来学习java里面的static了

要注意,java里面的static作用与C里面相差很大,不要联想记忆,应该分开记忆。
在这里,我们要先明确几点,
1.那就是一个类里面,无论是属性,还是方法,还是代码块(代码块我们后文会讲到),我们可以将它们分为静态的和非静态的。
二者的区别就是,静态的不依赖于实例化的对象,只与类有关,而非静态的就要依赖于实例化的对象(这里看不懂没关系,请接着往下看)。
2.对于调用一个类来说,它有以下几步
1.类加载:所有的static 修饰的均在类加载的阶段就执行
2.类的实例化,才到分配内存空间和调用构造方法。
因此,要注意到类加载是在类的实例化之前的

static修饰属性

当我们在一个类里面用static修饰了一个属性(成员变量)的时候,我们可以做一个小实验来帮助读者了解静态成员变量有啥特别的。

请看下图代码

class Count{       static int count1 ;//默认值为0    int count2;//默认值为0    public void add(){    count1++;count2++; }}public class Test {       public static void main(String[] args) {           Count count1 =new Count();        Count count2 =new Count();        Count count3 =new Count();        Count count4 =new Count();        count1.add();        System.out.println(count1.count1+" "+count1.count2);        count2.add();        System.out.println(count2.count1+" "+count2.count2);        count3.add();        System.out.println(count3.count1+" "+count3.count2);        count4.add();        System.out.println(count4.count1+" "+count4.count2);    }}//输出结果://1 1//2 1//3 1//4 1

从实验结果我们可以发现,static修饰的成员变量是所有类共享的,而非静态的成员变量是每个类互不关联的

这也就是为什么上文说静态是不依赖于实例化对象的,因此,static修饰的成员变量又叫类变量,而在我们观察上面的代码中我们会发现在count1.count1,count2.count1这样的代码的时候,编译器是给出了警告的,虽然我们仍能运行通过。
所以要注意:正确的访问静态成员变量,静态成员方法都是通过类名去访问,而不是通过对象的引用去访问
如下面的代码

class Count{       static int count1 ;    int count2;    public void add(){    count1++;count2++; }}public class Test {       public static void main(String[] args) {           Count count1 =new Count();        Count count2 =new Count();        Count count3 =new Count();        Count count4 =new Count();        count1.add();        System.out.println(Count.count1+" "+count1.count2);//前者是通过类名来访问静态成员变量,后者是通过引用来访问非静态成员变量        count2.add();        System.out.println(Count.count1+" "+count2.count2);        count3.add();        System.out.println(Count.count1+" "+count3.count2);        count4.add();        System.out.println(Count.count1+" "+count4.count2);    } }

static修饰方法

static 修饰的成员变量叫做类变量,同样,static修饰的成员方法叫做类方法。同样是只与类有关,同样是通过类名去调用。

举个例子

class Count{       static int count1 ;    int count2;    public void add(){    count1++;count2++; }    public static void sub(){   count1--;}}public class Test {       public static void main(String[] args){           Count.sub();//通过类名调用静态方法        System.out.println(Count.count1);//通过类名调用静态成员变量    }}//输出结果:-1

要注意几点

1.要注意到static是优先执行的,而实例化是后执行的,因此,static修饰的方法里面不能调用实例成员变量和实例成员方法。
2.实例成员方法和静态成员方法里面也都不能定义静态成员变量
3.this和super两个关键字不能在静态上下文中使用(this 是当前实例的引用, super是当前实例父类实例的引用, 也是和当前实例相关).

static修饰代码块

static修饰的代码块即为静态代码块,这里先不说,再下文与实例代码块进行对比说明。

static修饰类

这里我们先不说,等后面再说。学习到这里的时候我们只需先了解上述三种即可。

代码块

普通代码块

普通代码块就是定义在方法中的代码块。举个例子

public static void main(String[] args) {           {               int x1 = 10;            x1++;            System.out.println(x1);        }//这就是普通代码块,但一般用的很少        int x2 =20;        x2++;        System.out.println(x2);   }

构造(实例)代码块

构造(实例)代码块 : 定义在类中的代码块(不加修饰符),构造代码块一般用于初始化实例成员变量。举个例子

class Phone{       public String name;    public double price;    {           name = "huawei";        price =5000;    }//这样就叫实例(构造)代码块}

静态代码块

即用static修饰的代码块,静态代码块一般用来初始化静态成员变量。举个例子

class Phone{       public String name;    public double price;    static String origin;    {           name = "huawei";        price =5000;    }//实例代码块    static {           origin = "Chinese";    }//静态代码块}

实例代码块、静态代码块和构造方法执行的先后顺序

了解了啥是静态代码块和实例代码块,我们接下来要了解的比较重要的一点就是它们两与构造函数的先后执行关系。

下面做个小实验。

class Model{       int x;    static int count;    public Model(){           count=1;        System.out.println(count);    }    {           count =2;        System.out.println(count);    }    static{           count=3;        System.out.println(count);    }    {           count =4;        System.out.println(count);    }    static{           count =5;        System.out.println(count);    }}public class Test {       public static void main(String[] args) {           Model model1 = new Model();    }}//输出结果://3//5//2//4//1

通过上述实验我们发现,在实例代码块,静态代码块,构造方法,这三个选择里面,优先级:静态代码块>实例代码块>构造方法

而在所有的静态代码块,或者实例代码块里面,又按照从上到下的顺序依次执行。

静态代码块、实例代码块和构造方法的执行次数

除了三者的优先级,我们还需要了解它们的执行次数。

在上文中关于static的解释中,我们提到了静态是不依赖于对象的,静态是属于类的,所有类共同享有同一个静态。
那么就有了关于它们的执行次数的疑惑,接下来我们仍旧是做个小实验

class Model{       int x;    static int count;    public Model(){           count=1;        System.out.println(count);        System.out.println("**************");    }    {           count =2;        System.out.println(count);    }    static{           count=3;        System.out.println(count);    }    {           count =4;        System.out.println(count);    }    static{           count =5;        System.out.println(count);    }}public class Test {       public static void main(String[] args) {           Model model1 = new Model();        Model model2 = new Model();    }}//输出结果://3//5//2//4//1//**************//2//4//1//**************

我们发现在我们对类进行第二次实例化的时候,所对应的static 代码块,并没有执行,因此,我们得出一个结论

静态代码块只会在类的第一次实例化的时候执行,之后不会执行。而实例代码块和构造方法每次都会执行

了解匿名对象

匿名对象有点像C语言里面的匿名结构体,具体演示代码如下

Model model1 = new Model();        new Model();//这就是匿名的对象

匿名的对象和C语言里面的匿名结构体一样,就是当你只使用这个对象一次的时候你可以这样做,但是一般不常用。

到这,我们对于java里面的类和对象的学习就到此为止,文章如有错误,欢迎指正。

如果文章对你有所帮助的话,一键三连哦!

感谢观看
上一篇:应急响应
下一篇:java第四话:数组知识点讲解

发表评论

最新留言

很好
[***.229.124.182]2025年03月25日 13时12分59秒