Java 基础语法爆肝1W字只为弄懂类和对象

Posted 吞吞吐吐大魔王

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 基础语法爆肝1W字只为弄懂类和对象相关的知识,希望对你有一定的参考价值。


其实在学习 C 语言时就一直听到 C 语言是面向过程的,而 Java 是面向对象的这句话。并且我们之前的学习中也遇到了类,但是对它好像没有其他的认知。

那么面向过程与面向对象到底是什么呢?它们有哪些不同的意义呢?

类与对象到底是什么呢?

这章就来带你揭晓!

一、类与对象的初步认知

我们可以举一个洗衣服的例子来认识面向对象和面向过程

  • 对于面向过程: 我们可以看成是手洗衣服的过程

  • 对于面向对象: 我们可以看作直接用洗衣机去洗

    其中总共有四个对象:人、衣服、洗衣粉、洗衣机
    而整个洗衣服的过程就是:人将衣服放进洗衣机、人倒入洗衣粉、人启动洗衣
    因此整个过程主要就是上述四个对象之间交互完成的,人不需要关心洗衣机具体是如何洗衣服并且甩干的

因此对于面向对象,重点就是

  1. 找对象
  2. 创建对象
  3. 使用对象

那么对象从何而来呢?它其实是由类的实例化产生的

下面让我们来了解类以及类的实例化吧!

二、类和类的实例化

就是一类对象的统称

对象

就是这一类具体化的一个实例

举个栗子讲讲

比如我们做月饼的模子就是一个类,而通过这个模子可以做出月饼。那么在这个例子中,做月饼的模子就是类,而那个月饼就是对象。并且一个月饼就是一个实体,而一个模子可以实例化无数个对象,也就是说一个类可以产生无数的对象

那怎么声明一个类呢?首先我们要知道

  • 声明一个类就是创建一个新的数据类型(感觉类似于 C 语言中的 struct)
  • 类在 Java 中属于引用类型
  • Java 使用关键字 class 来声明类,并且类中可以定义一些属性和行为

上个代码看看

// 定义一个类
class Person{
    // 属性(成员变量)
    public int age;
    public String name;
    // 行为(成员方法)
    public void eat(){
        System.out.println("吃饭");
        int a = 10;	// 局部变量
    }
}

其中 Person 就是类名,{} 中的就是类的主体。类里面可以创建属性和行为

注意

此处写的方法不带关键字 static

类的实例化

用类类型创建对象的过程,称为类的实例化

我们要理解

  • 类只是一个模型一样的东西,限定了类有哪些成员变量
  • 一个类可以实例化出多个对象,实例化出的对象会占用实际的物理空间,存储类成员变量
  • 就如上述的月饼的例子,类就如一个模子,并没有实际的存在,实例化出的对象才能实际存储数据,占用物理空间

上个代码看看

// 定义一个类
class Person{
    // 属性(成员变量)
    public int age;
    public String name;
    // 行为(成员方法)
    public void eat(){
        System.out.println("吃饭");
        int a = 10;	// 局部变量
    }
}
public class Main{
    public static void main(String[] args){
        // 通过 new 实例化一个对象
        Person person = new Person();
        // 成员方法调用需要使用对象的引用调用
        person.eat();
    }
}
// 结果为:吃饭

其中 Person 为我们创建的类,person 为我们使用 Person 类创建的引用类型。关键字 new 用于创建一个对象的实例。使用 . 符号来访问对象中的属性和方法(既包含读,也包含写)

我们可以看一下在内存中上述代码是怎么存储的

注意 Person 类中定义的 a 是一个局部变量,因为它在方法里面。而局部变量保存在栈中,而实例化的对象以及该类中的类成员变量,保存在堆中

三、类的成员

类的成员可以包含:字段、方法、代码块、内部类和接口等

1. 字段/属性/成员变量

在类中但是在方法外部定义的变量,我们称为:“字段”或“属性”或“成员变量”(一般不严格区分)

我们可以对上述创建的对象进行调用

class Person{
    public int age;
    public String name;
}
public class Main{
    public static void main(String[] args){
        Person person = new Person();
        System.out.println("age = " + person.age);
        System.out.println("name = " + person.name);
    }
}
// 结果为:
// age = 0
// name = null

结果居然为 0 和 null,这是因为在 Java 中有一个默值认规则。

如果一个对象的字段没有设置初始值,那么就会被设置为一个默认的值

  • 对于各类数字类型,默认值为0 或者 0.0
  • 对于 boolean 类型,默认值为 false
  • 对于引用类型(String、Array、以及自定制类),默认值为 null
  • 对于 char 类型,默认值为 ‘\\u0000’

因此我们要注意,如果字段本身没有初始值,且使用前没有初始化,可能调用时会出现异常(使用引用类型时),如

class Person{
    public int age;
    public String name;
}
public class Main{
    public static void main(String[] args){
        Person person = new Person();
        System.out.println(person.name.length);
    }
}
// 会出现 NullPointerException 异常

2. 方法

方法其实之前就专门讲过了,这里就特意讲两点

  • 如果我们要想知道我们的对象里面有什么变量、值为多少,就类似于要做一个 show 方法去展示。但是如果想看的类很多,就很麻烦,这是我们可以使用一下步骤(编译器:IDEA)

    1. 在该类的空白处,点击右键就可以看到 Generate

    2. 再点击它,再找到 toString() 再点击就会出现以下代码(以上步骤可以使用快捷键:Alt + Insert 实现)

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

    其中 @Override 叫做重写的注解,就解释了这段代码是重写的,又 toString 属于 objet 的方法,所以就是重写了 object 方法

    为什么要这么做呢?如果我们直接通过

    System.out.println(person);
    // 结果为:Person@1b6d3586
    

    但是如果我们讲上述步骤完成后结果就变成了

    // 结果为:Person{age=0.0, name='null'}
    

    这是因为上述步骤修改了 object 类中的 toString 方法

    我们可以先按住 ctrl 再点击 println 方法,我们可以看到

    可以知道,x 被 valueOf 方法转换成了一个字符串,我们再跳到它的源码去可以看到

    这时我们看到了 toString,继续跳转到它的源码

    这里我们与不重写前的结果比较可以知道 toString 方法 @ 后一段的内容应该就是表示地址

    因此我们通过上面步骤对 toString 进行重写,就可以直接通过打印对象来得到该对象中的参数。并且我们将重写内的值改变,打印结果也会改变,如

    @Override
        public String toString() {
            return "Person{" +
                    "年龄=" + age +
                    ", 名字='" + name + '\\'' +
                    '}';
    

    打印结果就会改变为

    // 结果为:Person{年龄=0.0, 名字='null'}
    
  • 还有一点是关于构造方法的,下面会讲到!

3. static 关键字

上述的成员变量以及方法其实都是普通的成员变量以及方法,在 Java 中还有一种静态成员变量(也叫类变量)和静态成员方法。它们是由 static 修饰的

那么 static 有什么作用呢?

  1. 修饰属性
  2. 修饰方法
  3. 代码块
  4. 修饰类
  1. 修饰属性

    如果在成员变量前加上 static,此变量就叫做静态变量

    • 静态变量属于类,和具体的实例无关。也就是同一个类的不同实例公用一个静态属性
    • 可以直接调用静态变量,而无需创建类的实例
    • 静态变量存储在方法区

    我们来看一个代码

    class Person{
        public static int cnt;
        public static void speak(){
            System.out.println("我是静态成员方法!");
        }
    }
    public class Main{
        public static void main(String[] args){
            System.out.println("cnt = " + Person.cnt);
            Person.speak();
        }
    }
    // 打印结果为:
    // cnt = 0
    // 我是静态成员方法!
    

    大家注意没,我调用时是直接使用的类名,而不是对象名。这就是静态变量与普通成员变量的第一点不同,调用时直接使用类名。

    既然和实例无关那会不会静态变量的存储也会不同,我们开看一个代码

    class Test{
        public int a;
        public static int count; 
    }
    public class Main{
        
        public static void main(String[] args) {
            Test t1 = new Test();
            t1.a++;
            Test.count++;
            System.out.println(t1.a);
            System.out.println(Test.count);
            System.out.println("============");
            Test t2 = new Test();
            t2.a++;
            Test.count++;
            System.out.println(t2.a);
            System.out.println(Test.count);
        }
    }
    // 结果为:
    /**
    1
    1
    ============
    1
    2*/
    

    这是因为 count 被 static 修饰后,所有类共享,并且其存储区域在方法区

  2. 修饰方法

    • 静态方法属于类,而不属于类的对象
    • 可以直接调用静态方法,而无需创建类的实例
    • 静态方法只能访问静态数据成员,并且可以更改静态数据成员的值

    看一段代码

    class Person{
        int a;
        public static int cnt;
        public static void speak(){
            cnt = 10;
            //a = 100;	会报错,因为访问量非静态数据成员
            System.out.println("我是静态成员方法!");
        }
    }
    

    注意

    this 和 super 两个关键字不能在静态上下文中使用(this 是当前实例的引用, super 是当前实例父类实例的引用,也是和当前实例相关)【后面会介绍到!】

4. 总结

就用一段代码作为总结吧

class Person{
    public int age;	// 实例变量(属于对象)
    public static int count;	// 静态变量,编译时已经产生(属于类本身),只有一份且存放在方区
    public final in SIZE = 10;	// 被 final 修饰的叫常量,后续不可以更改(属于对象)
    public static final in COUNT = 99;	// 静态的常量(属于类本身)
    // 实例成员函数
    public void eat(){
        int a = 10;	//局部变量(存放在栈中)
    }
    // 静态成员函数
    public static void staticTest(){
        //不能访问非静态成员
        // age = 10;	会报错
        System.out.println("StaticTest()");
    }
}
public class Main{
    public static void main(String[] args){
		// 产生对象 实例化对象
        Person person = new Person();// person 为对象的引用
        System.out.println(person.age);// 默认值为0
        //System.out.println(person.count);// 会有警告!
        // 正确访问方式:
        System.out.println(Person.count);
        System.out.println(Person.COUNT);
        Person.staticTest();
        // 总结:所有被 stati c所修饰的方法或者属性,全部不依赖于对象。
        person.eat();
    }
}

emmmm,问一下,为啥 main 函数是静态的,如果是非静态的可以啵,比如

class TestDemo{
    
    public void main(String[] args){
        TestDemo testDemo = new TestDemo();
        testDemo.main();
    }
}

按照非普通成员方法的形式,如果 mian 函数要调就是上述代码吧。但大家发现一个问题没?

如果此时要使用 main 方法,就需要使用对象调用,那么好我们就在 main 方法里创建对象并且调用好了吧。诶?不对呀,要调用 main 方法就要使用对象啊???可我们创建的对象在 main 方法里面,怎么调用???

所以 main 方法要加上 static !

还有一点就是静态方法里面可以调用普通方法吗?no!

  1. 调用普通方法,就要用对象的引用
  2. 而静态方法的使用是直接使用类,不需要创建对象
  3. 所以静态方法里不能使用普通方法

四、封装

其实上面关于类主要也就是讲了类的实现和类的调用。如果我们以后使用了别人实现的类,结果后来别人修改了里面的某个变量名。人傻了?我们要一个一个修改原有的变量名吗?

因此出现了一种方法叫做:封装

封装的本质就是让类的调用者不必太多了解类的实现者是如何实现类的,只要知道如何使用就行

1. private 实现封装

private / public 这两个关键字表示访问权限控制

  • 被 public 修饰的成员变量或者成员方法,可以直接被类的调用者使用
  • 被 private 修饰的成员变量或者方法,不能直接被类的调用者使用

如果我们使用 public 修饰,那么类的实现的代码被修改了,可能你创建的代码就要花很多精力去维护。因此在实际中,我们一般用 private,至于 public 的使用要视情况而定,并且最好一个类只提供一个必要的 public 方法

让我们看一个代码更好的理解上述意思

class Person{
    private int age = 13;
    private String name = "ZhangSan";
    public void show(){
        System.out.println("name = "+name + " age = "+age);
    }
    }
}
public class Main{
    public static void main(String[] args){
        Person person = new Person();
        person.show();
    }
}
// 结果为:name = ZhangSan age = 13

上述代码就是使用了 private 修饰,所以主类里面不可以使用 age 和 name,而当我们要输出它们时,就可以直接使用 show 方法,无论实现 Person 类的函数中的 name 和 age 怎么改变,都可以正常打印。

如果你想获取或者修改这个 private 属性,那么就要用到接下来介绍的 getter / setter 方法

2. getter 和 setter 方法

使用这个方法可以在所创建的类中空白处右击鼠标,选择 Generate,就会出现

然后点击就可以。Getter 是获取这个属性,Setter 是修改这个属性,我们用上述代码示范

class Person{
    private double age;
    private String name;
    // 使用 setter 和 getter 方法
    public double getAge() {
        return age;
    }

    public void setAge(double age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Main{
    public static void main(String[] args){
        Person person = new Person();
        person.setAge(13);
        person.setName("张三");
        double age = getAge();
        String name = getName();
        System.out.println("姓名:" + name + " 年龄:" + age);
    }
}
// 打印结果为:姓名:张三 年龄:13

注意

大家有注意没,上述 setter 方法的代码

public void setAge(double age) {
   this.age = age;
}

其中 this 是啥,为啥要用它呢?首先其实这份代码可以改成这样

public void setAge(double Myage) {
   age = Myage;
}

其中我将形参修改了,反正 Myage 就表示我要修改成的值,如果我不修改,直接这样可以吗

public void setAge(double age) {
   age = age;
}

应该不行对吧,因为此时的 age 就表示该方法里面的了,不能将对象的值进行修改。所以为我们此时引入 this

this 表示当前对象的引用

五、构造方法

我们要知道,使用对象时,new 执行了两个过程

  1. 为对象分配内存
  2. 调用对象的构造方法

第二点的构造方法是啥???其实我们上述代码都都用到啦!

因为如果你自己没有创建构造方法的话,编译器会默认提供一个不带参数的构造方法

那什么又是不带参数的构造方法呢?

1. 基本语法

首先我们要知道构造方法的语法规则

  • 方法名必须与类名相同
  • 构造方法没有返回值
  • 每一个类中至少定义一个构造方法(没有明确定义,系统会自动生成一个无参构造,如果自己定义了,默认构造将不再生成)

我们来看一个构造无参的构造方法吧!

class Person{
    private double age;
    private String name;
    
    public Person(){
        System.out.println("这是一个无参的构造方法");
    }
}
public class Main{
    public static void main(String[] args){
        Person person = new Person();
    }
}
// 结果为:这是一个无参的构造方法

既然有无参数的,那么也有有参数的

class Person{
    private double age;
    private String name;
    public Person(){
        System.out.println("这是一个无参的构造方法");
    }
    public Person(double age, String name){
        this.age = age;
        this.name = name;
        System.out.println("这是一个有参的构造方法");
    }
    public void show(){ 
		System.out.println("name: "+name+" age: "+age); 
 	}
}
public class Main{
    public static void main(String[] args){
        Person person1 = new Person();
        Person person2 = new Person(13, "张三");
        person2.show();
    }
}
// 结果为:
// 这是一个无参的构造方法 
// 这是一个有参的构造方法
// name:张三 age:13

小结

  • 构造方法不仅仅可以构造对象,同时也可以帮助我们进行成员变量的初始化

  • 上述代码里面的构造方法其实构成了重载(方法名相同、参数列表不同、返回值不做要求)

2. this 关键字

其实上面已经讲了 this,但是大家要格外注意,this 是表示当前对象的引用(注意不是当前对象)原因如下

对象的形成要经过两步:1. 为对象分配内存 2. 调用合适的构造方法

而我们使用 this 时,其实我们已经完成了内存的分配,但我们并没有完成构造方法的调用,所以此时还不能说创建了对象,只是将对象的地址得到了,也就是对象的引用

this 的用法

  • this.成员变量:调用成员变量
  • this.成员方法:调用成员方法
  • this() :调用其他的构造方法

其中调用成员变量我们上面用了很多了,接下来我们先看下调用成员方法吧!

class Person{
    private double age;
    private String name;
    public Person(double age, String name){
        this.age = age;
        this.name = name;
        System.out.println("这是一个有参的构造方法");
    }
    public void show(){ 
		System.out.println(以上是关于Java 基础语法爆肝1W字只为弄懂类和对象的主要内容,如果未能解决你的问题,请参考以下文章

Java 基础语法爆肝两万字解析 Java 的多态抽象类和接口

❤️❤️❤️情人节必备,和女友一起玩新版飞机大战!万字只为你❤️❤️❤️收藏起来吧

全网首发,一篇文章带你走进pycharm的世界----别再问我pycharm的安装和环境配置了!!!万字只为君一笑,赶紧收藏起来吧

零基础学java-类和对象

还看不懂Python OpenCV?不,我不允许!隔壁大爷都说看得懂!❤️环境配置+问题分析+视频图像入门❤️万字只为你~

坚持原创 绝不注水爆肝万字长文Java 语言的基本特性(喂饭式教程)