java语法最重要的类和对象精讲


类和对象的初步认识

在讲类和对象之前,我们我们需要先真正理解什么是面向对象和面向过程
我们以洗衣服举例:
在这里插入图片描述
面向过程,如上图,你洗衣服关注的是每一步我应该干什么,有严格的顺序性。

在这里插入图片描述
面向对象,如上图,洗衣服是“人”、“衣服”、“洗衣机”、“洗衣粉”四个对象交互完成的,你不用关心衣服扔进洗衣机然后洗衣机怎么洗,你只需要把洗衣粉、衣服给洗衣机即可,然后洗衣机开始洗衣服。

面向对象你只需要把握以下三点即可编程
1.找对象
2.创建对象
3.使用对象

那对象是怎么来呢?对象是由类而来的
比如说:我们是人嘛,每个人都有共同的属性——姓名、性别、年龄。。。
也有很多共同的行为——吃饭、睡觉、上学、玩游戏。。。
那由这些共同的属性和行为我们可以放到一个模板(我们可以称这个模板为人)里,由这个模板可以产生很多的对象,而这个模板也就是一个类。

以大白话说就是:人类是一个类(它不是一个实体),单独到个人(实体)是一个对象。

创建对象是通过new关键字——把类实例化
在这里插入图片描述
(图片来自比特就业课)


提示:以下是本篇文章正文内容,下面案例可供参考

一、类和对象的实例化

基本语法

//创建类
Class 类名称{
    
    
   field;//成员属性
   method;//成员方法
}
//实例化对象
类名称 对象名 =new 类名称();

实战举例

class Person{
    
    //一个类由属性和字段组成
    //属性(也叫字段、成员变量)
    //成员变量又分1.普通成员变量,2.静态成员变量(后面说)
    public  String name;//普通成员变量
    public int age=18;//属性是可以赋值的,但不建议
    //因为后面创建对象是单独的个体,每个个体属性不一定全一样
    public int num;
    //方法(行为)
    //方法也分1.普通成员方法,2.静态成员方法
    public void eat(){
    
    
        System.out.println(name+"正在吃饭");
    }
    public void sleep(){
    
    
        System.out.println(name+"正在睡觉");
    }
}
public class test {
    
    
    public static void main(String[] args) {
    
    
        int      a     =1;
        Person xiaoHei=null;
        //和上面int a =1一样,Person是一种类型,xiaoMing是一个变量,然后我们可以给它赋初值
        //注意:由类定义的变量是一个引用变量,初值可以给null
        Person  xiaoMing=new Person();//由Person这个类实例化一个对象
        //成员变量age怎么访问?
        System.out.println(xiaoMing.age);//打印18
        //我们前面说过xiaoMing是一个引用变量,引用了Person这个对象
        //对象里包含了name、age等等成员变量
        //我们可以通过xiaoMing.age来访问age
        System.out.println(xiaoMing.name);//如果没有赋值直接打印是打印null
        System.out.println(xiaoMing.num);//如果没有赋值直接打印是打印0
        //解释:
        //整形:byte、short、int、long默认值0
        //浮点型:double、float默认值为0.0
        //字符型:char默认值"\u0000"
        //布尔型:boolean默认值false
        //引用数据类型:数组、类、接口默认值null

        //既然可以访问这些成员变量,我们就可以给它赋值
        xiaoMing.num=1000;
        System.out.println(xiaoMing.num);//打印1000
        xiaoHei=new Person();
        System.out.println(xiaoHei.num);
        //打印0,同一个类的创建的不同对象是不影响的,
        // 比如你给小明一个号码牌,不代表所有人类都有这样一个号码牌
        //其他人(对象)如果没有给num赋值,他们的num依旧默认是0
        
        //调用方法
        xiaoMing.eat();//引用变量名.方法() 即可
    }
 }

普通成员变量需要

二、类的成员

2.1字段/属性/成员变量

代码如下(示例):

class Person{
    
    //一个类由属性和字段组成
    //属性(也叫字段、成员变量)
    //成员变量又分1.普通成员变量,2.静态成员变量(后面说)
    public  String name;//普通成员变量
    public int age=18;//属性是可以赋值的,但不建议
    //因为后面创建对象是单独的个体,每个个体属性不一定全一样
    public int num;
}
public class test {
    
    
    public static void main(String[] args) {
    
    
        int      a     =1;
        Person xiaoHei=null;
        //和上面int a =1一样,Person是一种类型,xiaoMing是一个变量,然后我们可以给它赋初值
        //注意:由类定义的变量是一个引用变量,初值可以给null
        Person  xiaoMing=new Person();//由Person这个类实例化一个对象
        //成员变量age怎么访问?
        System.out.println(xiaoMing.age);//打印18
        //我们前面说过xiaoMing是一个引用变量,引用了Person这个对象
        //对象里包含了name、age等等成员变量
        //我们可以通过xiaoMing.age来访问age
        System.out.println(xiaoMing.name);//如果没有赋值直接打印是打印null
        System.out.println(xiaoMing.num);//如果没有赋值直接打印是打印0
        //解释:
        //整形:byte、short、int、long默认值0
        //浮点型:double、float默认值为0.0
        //字符型:char默认值"\u0000"
        //布尔型:boolean默认值false
        //引用数据类型:数组、类、接口默认值null

        //既然可以访问这些成员变量,我们就可以给它赋值
        xiaoMing.num=1000;
        System.out.println(xiaoMing.num);//打印1000
        xiaoHei=new Person();
        System.out.println(xiaoHei.num);
        //打印0,同一个类的创建的不同对象是不影响的,
        // 比如你给小明一个号码牌,不代表所有人类都有这样一个号码牌
        //其他人(对象)如果没有给num赋值,他们的num依旧默认是0
    }
 }

2.2方法

class Person{
    
    
    //方法(行为)
    //方法也分1.普通成员方法,2.静态成员方法
    public void eat(){
    
    
        System.out.println(name+"正在吃饭");
    }
    public void sleep(){
    
    
        System.out.println(name+"正在睡觉");
    }
}
public class test {
    
    
    public static void main(String[] args) {
    
    
        Person  xiaoMing=new Person();//由Person这个类实例化一个对象
        //调用方法
        xiaoMing.eat();//引用变量名.方法() 即可
    }
 }

2.3static关键字

static修饰的静态成员变量

import java.util.Arrays;
class Person{
    
    
    public int age;
    public static int count;//静态成员变量(也是成员变量)
}
public class test {
    
    
   public static void main(String[] args) {
    
    
        Person p1=new Person();
        Person p2=new Person();
        p1.count++;
        p1.age++;
        System.out.println(p1.count);//打印1
        System.out.println(p1.age);//打印1

        p2.count++;
        p2.age++;
        System.out.println(p2.count);//打印2
        System.out.println(p2.age);//打印1
    }
    }

在这里插入图片描述

我们new了两个对象p1和p2,我们age++和count++由前面的知识可知道,不赋值的整形应该是默认为0,那我们打印的age和count应该都是1,而不同对象的age和count也应该是单独的、互补影响的,所以4个打印都应该是1啊,同样验证,普通成员变量age符合我们上面所说的要求,p1.age和p2.age打印的都是1。但是问题出现了,p2的count(静态成员变量)打印的是2

count默认是0,却打印了2,就好像p1.count和p2.count进行++操作的时候是对同一个内容进行操作一样,这不符合逻辑啊?但事实就是这样哈哈哈

静态成员变量又称类变量,被static修饰过后,count是被放到方法区中(不是堆区!)
ps:方法区会存储对应类的字节码文件(.class文件),还会存储静态的成员变量
在这里插入图片描述
静态成员变量不是对象的,它是类的,且在方法区中只有一个,不像对象,每一个对象一个对应的成员变量。

到这里也解释了,为什么我们用p1.count++和p2.count++会是对同一空间同一内容进行操作(count只有一个)。而p1.age,和p2.age是两个不同空间的不同的内容。

我们上面代码new p1 和new p2来调用count都是没有意义的,你可以直接使用类名Person来调用count,也间接的节约了创建对象所需的空间

static修饰的静态成员方法
直接用类就可以调用,其他的和普通成员方法没区别

class Person{
    
    
    public static void xiao() {
    
    
        System.out.println("haha");
    }
}
public static void main(String[] args) {
    
    
        Person.xiao();//打印haha,静态成员方法不需要new对象
    }

注意:静态的成员变量是不可以在普通方法中定义的

public static void m() {
    
    
        static int a=1;//这里会报错
    }

普通方法里能调用静态方法

public static void hello() {
    
    
        System.out.println("你好");
    }
    public  void print() {
    
    
        hello();//这里不会报错,可以正常调用
    }

静态方法里不能调用普通方法

class Person{
    
    
    public  String name;
    public int age;
    public static void staticFunc() {
    
    
        print();//这里会报错,静态方法内不允许调用普通方法
    }
    public  void print() {
    
    
        System.out.println("姓名"+name+"年龄"+age);
    }
}

原因很简单,静态方法是不依赖对象的,我们本是可以通过类名进行调用,但是普通成员变量是依赖对象的,你如果调用了staticFunc里的print,print里面的name和age哪里来的对象呢?

当然了,如果你非要抬杠,我就要在静态方法里调用普通方法怎么办,如下

class Person{
    
    
    public  String name;
    public int age;
    public static void staticFunc() {
    
    
        Person p=new Person();
        //你在静态方法里new一个对象
        person.print();
    }
    public  void print() {
    
    
        System.out.println("姓名"+name+"年龄"+age);
    }
}

再举一个相似的例子:

public class TestDemo {
    
    
    public static void func1() {
    
    
        
    }

    public  void func2() {
    
    
        
    }

    public static void main(String[] args) {
    
    
        func1();//静态方法可以直接调用
        //func2();//普通方法直接调用会报错
        //普通方法的 正确调用方式如下
        TestDemo test=new TestDemo();
        test.func2();
    }
}

综上:静态方法可以直接通过类名调用,普通方法则需要new一个对象
final修饰的常量/静态常量

class Test{
    
    
    public int a;
    public static int count;
    public final int size=1;//对象里
    //被final修饰的叫常量,也属于对象,后续不可更改,类似C语言const
    public final static int c=99;//在方法区
    //静态的常量,属于类本身,有且只有一份,被final修饰后续不可更改
}

一个对象存储在哪里和final无关,还是取决于是否有static
最后:注意——static定义的东西只能在类里面定义,不可以在方法里定义

小知识:main函数是不是静态static的都可以,取决于JVM的设计逻辑

2.4小结

1.静态成员变量的访问方式是通过类名.静态成员属性/方法,只要是静态的就是不依赖于对象的,因为你普通成员变量需要通过访问对象来实现,但是静态的直接通过类就可以进行访问了
2.普通成员方法调用需要对象的引用,但是static的静态成员方法可以直接通过类名来调用
3.静态的成员变量是不可以在方法中定义的(因为它是类变量)
4.普通方法里可以调用静态方法
5.静态方法里不可以调用普通方法
6.静态方法可以直接通过类名调用,普通方法则需要new一个对象

三、封装

3.1private实现封装

private/public两个关键字表示“访问权限控制”
1.被public修饰的成员变量或成员方法,可以直接被类的调用者使用。
2.被private修饰的成员变量或成员方法,不能被类的调用者使用

class Test{
    
    
    public String name;
}
class Name{
    
    
    private String name;
    //一旦属性或方法被private修饰,则该属性或方法就被封装起来了
    //封装的效果就是:被封装的属性或方法只能在当前的类中使用
}
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        Test t=new Test();
        t.name="abc";//没有被private修饰可以用
        Name n=new Name();
        n.name="abc";//被private修饰了,你再用就会报错
    }
}

被类Name中的name被private修饰后的,只能在当前的类中进行访问,打个不恰当的比方,现在疫情封城了,你只能在你所在的城市里活动,不允许出城。private就相当于封城令,而被修饰的属性/方法就是我们人,当前类就是我们当前的城市。

那说了这么多,我们为什么要用private呢?
1.我们用了private后,它修饰的属性或方法(你不想让别人拿到修改的东西)就会更加安全。
2.如果不用private,比如我之前的类Name中我们是用public String name;
如果哪天有人给我把类里面的name改成了myname,那后面很多程序用的时候是用的对象名.name,你现在name没了啊,难道还要一个一个去往下改吗?这显然不现实。用了private之后用户只需要调用你公开的方法(3.2的getter和setter方法)即可,你在类里面把name改成什么都可以。

注意事项:
1.private不光能修饰字段,也能修饰方法
2.通常情况下我们会把字段设为private属性,但是方法是否需要设计为public,就需要视情况而定,一般我们希望一个类只提供“必要的”public方法,而不是所有方法都无脑设为public

3.2getter和setter方法

我们3.1用private封装了属性或方法,那这个时候肯定会有小伙伴问,那如果我真的想要使用那个被封装的属性或方法呢?也很简单,代码如下:

class Name{
    
    
    private String name;
    //一旦属性或方法被private修饰,则该属性或方法就被封装起来了
    //封装的效果就是:被封装的属性或方法只能在当前的类中使用

    public String getName() {
    
    //获取Name的方式
        return name;
    }

    public void setName(String myName){
    
    
        name=myName;
    }
}
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        Test t=new Test();
        t.name="abc";//没有被private修饰可以用
        Name n=new Name();
        //n.name="abc";//被private修饰了,你再用就会报错
        n.setName("xiaoMing");//外部给name赋值
        String x=n.getName();//外部接收name
        System.out.println(x);//打印xiaoMing
    }
}

打印效果如下:
在这里插入图片描述
注意事项
1.getName即为getter方法,表示获取这个成员的值。
2.setName即为setter方法,表示设置这个成员的值。

四、构造方法

4.1基本语法

构造方法是一种特殊方法,方法名和类名是相同的,且没有返回值,它使用关键字new实例化新对象时会被自动调用,用于完成初始化操作。
new执行过程
1.为对象分配空间
2.调用对象的合适的构造方法
(合适就意味着构造方法不唯一)
语法规则
1.方法名称必须与类名称相同
2.构造方法没有返回值类型声明
3.每一个类中一定至少存在一个构造方法(没有明确定义,则系统自动生成一个无参构造)
示例如下:

class people{
    
    
    public people(){
    
    
        System.out.println("people()不带参数的构造方法");
    }
}

那我们怎么调用这个构造方法呢?

public static void main(String[] args) {
    
    
        people p=new people();
    }

new一个对象直接执行代码即可,打印效果如下:
在这里插入图片描述
再来看看带参数的构造方法及其使用方式

class people{
    
    
    public String name;
    public people(){
    
    
        System.out.println("people()不带参数的构造方法");
    }
    public people(String name){
    
    
        this.name=name;
        System.out.println("people(String)带String参数的构造方法");
    }
}
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        people p=new people("xiaoHei");
    }
}

new对象的时候把相应构造方法需要的参数放进去即可完成调用,打印效果如下:
在这里插入图片描述

冷知识:如果你原先的类里没有任何构造方法,那么编译器会默认生成一个不带有参数的构造函数。:

class people{
    
    
    
}
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        people p=new people();
    }
}

你执行上面的代码也不会报错,系统会默认为你生成如下代码

class people{
    
    
    public people(){
    
    
        
    }
}

注意事项
1.如果类中没有提供任何的构造函数,那么编译器会默认生成一个不带有参数的构造函数。
2.若类中定义了构造方法,则默认的无参构造将不再生成。
3.构造方法支持重载,规则和普通方法的重载一致。

4.2this关键字

我们来回顾一下3.1的代码

class Name{
    
    
    private String name;
    //一旦属性或方法被private修饰,则该属性或方法就被封装起来了
    //封装的效果就是:被封装的属性或方法只能在当前的类中使用
    public void setName(String myName){
    
    
        name=myName;//赋值方法一
    }
}

有时候我们可能不小心把形参写成了成员名一样

class Name{
    
    
    private String name;
    //一旦属性或方法被private修饰,则该属性或方法就被封装起来了
    //封装的效果就是:被封装的属性或方法只能在当前的类中使用
    public void setName(String name){
    
    
        name=name;//局部变量优先使用
    }
}

但上述的赋值方法是默认形参赋给形参,你并没有真正实现成员name的赋值,那如果我们写的函数形参就是和成员变量名一样呢?我们用this

class Name{
    
    
    private String name;
    //一旦属性或方法被private修饰,则该属性或方法就被封装起来了
    //封装的效果就是:被封装的属性或方法只能在当前的类中使用
    public void setName(String name){
    
    
        this.name=name;
        //this表示当前对象的引用
    }
}

注意事项
1.this.data表示调用当前对象的属性
2.this.func()表示调用当前对象的方法
3.this()调用当前对象的其他构造方法——this()只能存在于构造方法中
关于3,解释如下:

class people{
    
    
    public String name;
    public people(){
    
    
        this("hh");
        System.out.println("people()不带参数的构造方法");
    }
    public people(String name){
    
    
        this.name=name;
        System.out.println("people(String)带String参数的构造方法");
    }
}
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        people p=new people();
    }
}

我们从main函数进去,new了一个对象,开始调用不带参数的构造方法,不带参数的构造方法里有一个this(),this()表示调用当前对象的其他构造方法,也就说这里的this(“hh”)=people(“hh”),也就是进入了另一个构造方法 public people(String name),然后打印"people(String)带String参数的构造方法",出 public people(String name),回到public people(),打印"people()不带参数的构造方法"
一般的,我们会在当前对象属性/方法前加this.因为你加上一定没错,不加可能当前对象属性/方法会与形参相同导致错误。简言之,加上一定没错,不加可能有错。

五、认识代码块

字段的初始化方法有:
1.就地初始化
2.使用构造方法初始化
3.使用代码块初始化
前面两种我们已经学习过了,接下来我们介绍第三种方式,使用代码块初始化

5.1什么是代码块

使用{ }定义的一段代码
根据代码块定义的位置及关键字,又可分为以下四种:
1.普通代码块
2.构造代码块
3.静态块
4.同步代码块(后续讲解多线程部分继续学习)

5.2普通代码块

定义在方法中的代码块,直接用{ }定义,示例如下:

{
    
    
代码内容
}

5.3构造代码块

也叫实例代码块,示例如下:

class person{
    
    
private String name;//实例成员变量
private int age;
private String sex;
}
{
    
    //实例代码块
this.name="bit";
this.age=12;
this.sex="m";
}

实例代码块优先于构造函数运行。

5.4静态代码块

使用static定义的代码块,一般用于初始化静态成员属性。 示例如下:

static{
    
    
 count=10;//只能访问静态数据成员
}

5.5引申

class people{
    
    
    public String name;
    public people(){
    
    
        System.out.println("people()不带参数的构造方法");
    }
    public people(String name){
    
    
        this.name=name;
        System.out.println("people(String)带String参数的构造方法");
    }
    {
    
    
        System.out.println("实例代码块");
    }
    static{
    
    
        System.out.println("静态代码块");
    }
}
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        people p=new people();
    }
}

我们由构造方法那块知识知道,我们new了一个不带参数的对象然后直接运行,一定会得到不带参数的构造方法里面的一些东西,然后我们people这个类里面代码块的顺序是构造方法->实例代码块->静态代码块,但我们上面这段代码实际运行情况并不是这样
在这里插入图片描述
我们是优先运行了静态代码块,然后是实例代码块和构造方法。不管静态、实例、构造代码块在类中的顺序,静态代码块一定是先被打印,其次是实例代码块,最后是构造方法,如果大家都是相同类型(比如同是静态)的,这时才看的是代码顺序。
我们再细化看一下实例代码块和静态代码块的区别

class people{
    
    
    public String name;
    public people(){
    
    
        System.out.println("people()不带参数的构造方法");
    }
    public people(String name){
    
    
        this.name=name;
        System.out.println("people(String)带String参数的构造方法");
    }
    {
    
    
        System.out.println("实例代码块");
    }
    static{
    
    
        System.out.println("静态代码块");
    }
}
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
       people p1=new people();
        System.out.println("===============");
        people p2=new people();
    }
}

在这里插入图片描述
我们new了两个对象,但我们静态代码块永远只执行一次,剩下的实例代码块和构造方法均执行两次。
我们再来测试一下,不new对象的运行效果

class people{
    
    
public static int count;
    public String name;
    public people(){
    
    
        System.out.println("people()不带参数的构造方法");
    }
    public people(String name){
    
    
        this.name=name;
        System.out.println("people(String)带String参数的构造方法");
    }
    {
    
    
        System.out.println("实例代码块");
    }
    static{
    
    
        System.out.println("静态代码块");
    }
}
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
     System.out.println(people.count);
     System.out.println(people.count);
    }
}

在这里插入图片描述
静态代码块不需要实例化对象也可以运行,但是不管你执行几次,静态代码块只会执行一次。
静态代码块本质上来说:是初始化静态的东西的,它是不依赖于对象的

六、一些补充

6.1匿名对象

匿名只是表示没有名字的对象。
1.没有引用的对象成为匿名对象
2.匿名对象只能在创建对象时使用
3.如果一个对象只是用一次,后面不需要用了,可以考虑使用匿名对象。

class people{
    
    
    public void eat(){
    
    
        System.out.println("正在吃东西");
    }
    public void drink(){
    
    
        System.out.println("正在喝东西");
    }
}
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        new people().eat();
        new people().drink();
    }
}

在这里插入图片描述

上述这个代码,我们new了一个对象却没有把它赋给任何值,再用这个new出来的对象去调用eat函数。这种操作方法我们不需要名字,可以直接使用,就叫作匿名对象。但是有个缺点,就是你如果用匿名对象,每用一个需要对象的函数都要new一个匿名对象,比如我们上面调用两个函数eat和drink就new了两个对象的空间,比较浪费空间。

七、小结

1.一个类可以产生无数的对象,类就是模板,对象就是具体的实例。
2.类中定义的属性,大概可分为几类:类属性,对象属性。其中被static修饰的数据类型称为类属性,static修饰的方法称为类方法,特点是不依赖于对象,我们只需要通过类名就可以调用其属性或者方法。
3.静态代码块优先实例代码块执行,实例代码块优先构造函数执行。
4.this关键字代表的是当前对象的引用,不是当前对象。


猜你喜欢

转载自blog.csdn.net/m0_57180439/article/details/121062280