类和对象--java(超级详细,两万字!)

类和对象

一个对象的产生至少有以下两步:
1.为对象分配内存
2.调用合适的构造方法

1.面向对象的初步认知

1.1什么是面向对象

java是一门纯面向对象的语言(Object Oriented Program)简称OOP,在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。用面向对象的思想来设计程序,更符合人们对事物的认知,对于大型程序的设计,扩展以及维护都非常友好。

1.2面向对象与面向过程

什么是面向过程?
拿传统洗衣服举例:我们要
拿盆子–放水–放衣服–放洗衣粉–手搓–换水–放洗衣粉–手搓–拧干–晾衣服
传统的方式,注重的是洗衣服的过程,少了一个环节都不行

现代洗衣服
总共有四个对象:人,衣服,洗衣粉,洗衣机
整个洗衣服的过程:人将衣服放进洗衣机,倒入洗衣粉,启动洗衣机,洗衣机就会完成洗衣服并且甩干

这四个对象协作完成洗衣服这件事情
再比如说
你刚上班的时候,想买个手机,这个时候没人帮你买,你就要自己去上网查手机参数–打开电商平台–找到这个手机–下单–写地址–取快递–拆快递–验货(这个是面向过程)
等后来你成为了大老板,有了一个女秘书,这个时候你想买个手机,你只需要告诉你的女秘书,她就会帮你完成所有的步骤,你只需要跟秘书这个对象完成交接就行了(这个就是面向对象)

2.类定义和使用

面向对象程序设计关注的是对象,而对象是现实生活中的实体,比如洗衣机,但是计算机并不认识洗衣机,需要开发人员告诉计算机什么是洗衣机。
在这里插入图片描述
这个图片就是对洗衣机的简单描述,该过程称为对洗衣机对象(实体)进行抽象(对一个复杂事物的重新认知),但是这些简化的抽象结果计算机并不能识别,开发人员可以采用某种面向对象的编程语言来进行描述,比如java

2.1简单认识类

类是用来对一个实体(对象)进行描述的,主要描述该实体(对象)具有哪些属性(外观尺寸颜色等),哪些功能(用来干啥),描述完成后计算机就可以识别了
比如:洗衣机,它在java中就可以看成一个类
属性:品牌,型号,重量,尺寸,颜色…
功能:洗衣,烘干…

2.2类的定义格式

class ClassName//类名字采用大驼峰的形式,每个字母大写
{
	field;//字段(或者叫属性,或者叫成员变量)
	method;//行为或者成员方法
}

class为定义类的关键字,ClassName为类名,{}中为类的主体

2.3定义一个类(一般情况下一个java文件一个类,可以有多个)

每一个类对应一个字节码文件,下面的洗衣机类就会生成一个WashMachine.class的字节码文件
定义一个洗衣机类

class WashMachine
{
    //字段/属性/成员属性/成员变量(定义在类里,方法外面)
    public String brand;//品牌“小白”
    public String type;//型号
    public double weight;//重量
    public double length;//长度
    public double width;//宽度
    public double height;//高度
    public String color;//颜色


    //成员方法
    public void washClothes()
    {
        System.out.println("洗衣服");
    }
    public void dryClothes()
    {
        System.out.println("脱水");
    }
    public void setTime()
    {
        System.out.println("定时");
    }
}

采用java语言将洗衣机类在计算机中定义完成,经过javac编译之后形成.class文件,在JVM的基础上计算机就可以识别了
注意:
1.类目必须采用大驼峰的形式
2.成员前写法统一为public,后面会详细解释
3.此处写的方法不带static关键字,后面会详细解释
4.方法名称都是小驼峰(第一个单词小写,后面都大写)
5.public修饰的类必须和文件名相同
6.main方法所在的类一般要用文件名修饰

3.类的实例化

3.1什么是实例化

定义了一个类,就相当于在计算机中定义了一个新的类型,与int,double,类似,只不过int和double是java语言自己自带的内置类型,而类是用户定义了一个新的类型,有了这些自定义类型之后,就可以使用这些类来定义实例(或者叫定义对象)
用类的类型创建对象的过程,叫做类的实例化,在java中采用new关键字,配合类名来实例化对象

class WashMachine
{
    //字段/属性/成员属性/成员变量(定义在类里,方法外面)
    public String brand;//品牌“小白”
    public String type;//型号
    public double weight;//重量
    public double length;//长度
    public double width;//宽度
    public double height;//高度
    public String color;//颜色


    //成员方法
    public void washClothes()
    {
        System.out.println("洗衣服");
    }
    public void dryClothes()
    {
        System.out.println("脱水");
    }
    public void setTime()
    {
        System.out.println("定时");
    }
}
public class Class_And_Object
{

    public static void main(String[] args)
    {
        //washMachine是一个引用变量,指向new WashMachine()这个对象,washMachine存的是new WashMachine()的地址
        WashMachine washMachine=new WashMachine();
        //类型WashMachine  定义了一个变量washMachine,赋了一个new WashMachine()的值
        //也就是说washMachine这个对象里有WashMachine这个类里的所有属性
        washMachine.brand="小白";
        System.out.println(washMachine.brand);
        WashMachine washMachine2=new WashMachine();
        washMachine2.brand="小天鹅";
        System.out.println(washMachine2.brand);
    }
}

类和对象--java(超级详细,两万字!)_第1张图片

结论:可以使用new关键字实例化多个对象,多个对象之间互相不影响,因为它们都有一块独立的内存空间

class Person
{
    public String name;
    public int age;
    public void eat()
    {
        System.out.println(name+"在吃饭");
    }
}
 public static void main(String[] args)
    {
        Person person1=new Person();
        person1.name="张三";
        person1.eat();//这个时候会输出张三在吃饭,调用eat的时候name已经被赋值了

    }

3.2类和对象的说明

1.类只是一个模型一样的东西,用来对一个实体进行描述,限定了类有哪些成员
2.类是一种自定义模型,可以用来自定义变量
3.一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储成员变量
4.做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间

4.this引用

4.1为什么要有this引用

先来看一个日期类

class TestDate
{
    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 printDate()
    {
        System.out.println(year+"年"+month+"月"+day+"日");
    }

    public static void main(String[] args)
    {
        TestDate testDate1=new TestDate();
        TestDate testDate2=new TestDate();
        TestDate testDate3=new TestDate();
        testDate1.setDate(2022,5,11);
        testDate2.setDate(2023,6,3);
        testDate3.setDate(2024,10,18);
        testDate1.printDate();
        testDate2.printDate();
        testDate3.printDate();
    }
}

下面附上运行结果
在这里插入图片描述

类和对象--java(超级详细,两万字!)_第2张图片
问题来了:
问题1:setDay这个方法只有一个,我们赋值调用的时候怎么知道setDay调用的是对象testdata1的值,还是对象testdata2的值,还是对象testdata3的值呢
这个时候我们就要用this了

还有一个问题2:不多说,上代码
仔细观察这个代码块和上面那个代码块有什么不同

class TestDate
{
    public int year;
    public int month;
    public int day;
    public void setDate(int year,int month,int day)
    {
        year=year;
        month=month;
        day=day;
    }
    public  void printDate()
    {
        System.out.println(year+"年"+month+"月"+day+"日");
    }

    public static void main(String[] args)
    {
        TestDate testDate1=new TestDate();
        TestDate testDate2=new TestDate();
        TestDate testDate3=new TestDate();
        testDate1.setDate(2022,5,11);
        testDate2.setDate(2023,6,3);
        testDate3.setDate(2024,10,18);
        testDate1.printDate();
        testDate2.printDate();
        testDate3.printDate();
    }
}

这个图是这个代码块的运行结果
在这里插入图片描述
我们可以看到,两个代码块基本都一样,只不过第二个代码块把成员方法里面局部变量的名字都改成了跟成员变量的名字一样的了
类和对象--java(超级详细,两万字!)_第3张图片
就是上图这块
当我们把这段代码改成加上this的,也就是下图
类和对象--java(超级详细,两万字!)_第4张图片
它的运行结果就变成了
在这里插入图片描述
这是为什么呢
首先对于问题1的解释:
其他代码不动,只改图片内容
就这个代码来说,如果不改也对,但是加上了一定对,不改可能错,建议以后加上
类和对象--java(超级详细,两万字!)_第5张图片
我们可以看到,有三个对象被创造了出来,分别是testdata1,testdata2和testdata3,我们用哪个对象的时候,this就指代的哪个对象,这是编译器自己智能的一个体现
对于问题2:
不加this的时候,变量名字跟成员变量相同,这个时候编译器采用了局部优先原则,也就是在setDate后面的括号的变量是局部变量,因为两个都叫year,编译器这个时候看到局部有year了,就检测不到外部的成员变量year,就相当于局部变量year自己给自己赋值了,然后外部的成员变量year没被赋值,就是0
加上this之后,this.year指定了哪个对象的year要被赋值,如果调用第一个对象,就指定了第一个对象的成员变量year被赋值

4.2什么是this引用

this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都通过该引用去访问,只不过所有的操作对用户都是透明的,即用户不需要来传递,编译器自动完成
更深层次的理解就是编译器自动把这个变量隐藏了, 原本是在成员方法后面的一个参数值应该是如下两图所示类型,用来接收你传过来的调用的对象,编译器比较智能,自己隐藏了,你加不加这个(TestDate this)变量都行
在这里插入图片描述
在这里插入图片描述

4.3this引用的特性

1.this的类型:对应类的类型的引用,即哪个对象调用就是哪个对象的引用类型
2.this只能在成员方法中使用
3.在成员方法中,this只能引用当前对象,不能引用其他对象
4.this是成员方法第一个隐藏参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this负责来接受

5.对象的构造以及初始化

5.1如何初始化对象

如何初始化对象–(也就是)如何初始化对象当中的成员
在java方法内部定义一个局部变量时,必须初始化,否则会编译失效
在这里插入图片描述
上图就是错的,因为没给a赋值(初始化)
如果是对象,需要调用之前的setDate的方法才可以将具体的日期设置到对象中。
通过上述两个例子发现两个问题:
1.每次对象创建好之后调用setDate方法设置具体日期,比较麻烦,那对象该如何初始化呢?
2.局部变量必须要初始化才能使用,为什么成员变量声明之后没有赋值依然可以运行呢?
因此,我们引入了构造方法

5.2构造方法

5.2.1概念

构造方法,也成为构造器,是一个特殊的成员方法,名字必须跟类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次

class TestDate
{
    public int year;
    public int month;
    public int day;
    //构造方法可以重载
    TestDate()
    {
        System.out.println("没有参数的构造方法!");
    }
    TestDate(int year,int month,int day)
    {
        this.year=year;
        this.month=month;
        this.day=day;
        System.out.println("有三个参数的构造方法");
    }
    public void setDate(TestDate this,int y,int m,int d)
    {
        this.year=y;
        this.month=m;
        this.day=d;
    }
    public  void printDate(TestDate this)
    {
        System.out.println(year+"年"+month+"月"+day+"日");
    }

    public static void main(String[] args)
    {
        TestDate testDate1=new TestDate();
        TestDate testDate2=new TestDate();
        TestDate testDate3=new TestDate();
        testDate1.setDate(2022,5,11);
        testDate2.setDate(2023,6,3);
        testDate3.setDate(2024,10,18);
        testDate1.printDate();
        testDate2.printDate();
        testDate3.printDate();
    }
}

问题又来了,我们没写构造方法的时候也没有报错
解答:我们没有写构造方法的时候,编译器会自动给我们写一个不带任何参数的构造方法,如果我们写了构造方法,编译器就不给我们了
构造方法怎么调用呢?
就是下图
在这里插入图片描述
这个new TestDate()就是调用的构造方法,这个括号里面没有参数,就是调用的不带参数的构造方法,如果类似new TestDate(2022,1,30)的话,就是调用带三个参数的构造方法

class TestDate
{
    public int year;
    public int month;
    public int day;
    TestDate()
    {
        System.out.println("没有参数的构造方法!");
    }
    TestDate(int year,int month,int day)
    {
        this.year=year;
        this.month=month;
        this.day=day;
        System.out.println("有三个参数的构造方法");
    }
    public void setDate(TestDate this,int y,int m,int d)
    {
        this.year=y;
        this.month=m;
        this.day=d;
    }
    public  void printDate(TestDate this)
    {
        System.out.println(year+"年"+month+"月"+day+"日");
    }

    public static void main(String[] args)
    {
        TestDate testDate1=new TestDate(2022,5,11);
        TestDate testDate2=new TestDate(2023,6,3);
        TestDate testDate3=new TestDate();
        //testDate1.setDate(2022,5,11);
        //testDate2.setDate(2023,6,3);
        //testDate3.setDate(2024,10,18);
        testDate1.printDate();
        testDate2.printDate();
        testDate3.printDate();
    }
}

假如下面两个代码块拼装起来,就错了
类和对象--java(超级详细,两万字!)_第6张图片
在这里插入图片描述
因为你没有构造方法的时候,编译器给你一个不带参数的构造方法,你有了,就不给你了,所以你的new后面的括号必须赋值,跟你自己所创建的构造方法相对应
如果下面两个代码块想结合,运行出来的结果就是2024.10.18
类和对象--java(超级详细,两万字!)_第7张图片
在这里插入图片描述
所以我们以后初始化的时候直接用构造方法就可以了,不用费事的写一个成员方法再初始化了

在没有参数的构造方法当中用this引用直接给对象的成员变量(属性)赋值也可以,上代码!

class Student
{
    public int age;
    public String name;
    Student()
    {
        this.age=10;
        this.name="小白";
        System.out.println("没有参数的构造方法!");
    }
    public void print()
    {
        System.out.println("年龄:"+this.age+" "+"姓名:"+this.name);
    }
}
public class Class_And_Object2
{
    public static void main(String[] args)
    {
        Student student=new Student();
        student.print();
    }
}

那么我们如何在没有参数的构造方法里面调用有参数的构造方法呢?
上代码!

class Student
{
    public int age;
    public String name;
    Student()
    {
        this(18,"小白");
        System.out.println("没有参数的构造方法!");
    }
    Student(int age,String name)
    {
        this.age=age;
        this.name=name;
    }
    public void print()
    {
        System.out.println("年龄:"+this.age+" "+"姓名:"+this.name);
    }
}
public class Class_And_Object2
{
    public static void main(String[] args)
    {
        Student student1=new Student();
        student1.print();
        Student student2=new Student();

    }
}

类和对象--java(超级详细,两万字!)_第8张图片

注意:
1.this()只能在构造方法里面调用构造方法,不能把this()放在一个普通的方法里面调用构造方法!如下图
类和对象--java(超级详细,两万字!)_第9张图片

2.构造方法当中,不能调用自己,如下图
类和对象--java(超级详细,两万字!)_第10张图片
3.只能放在构造方法内部的第一行
类和对象--java(超级详细,两万字!)_第11张图片
4.不能形成环,比如在第一个构造方法里面调用第二个,再在第二个构造方法里面调用第一个。

另外,this除了可以在构造方法里面调用构造方法和this.成员变量之外,还可以在方法里面调用另外一个方法,这个this可以不用放在第一行
类和对象--java(超级详细,两万字!)_第12张图片

在IDEA中怎么让编译器自己生成构造方法呢?
首先要公开
类和对象--java(超级详细,两万字!)_第13张图片
类和对象--java(超级详细,两万字!)_第14张图片
按住Shift多选
类和对象--java(超级详细,两万字!)_第15张图片

就自动生成成功了

5.2.2特性

1.名字必须与类名相同
2.没有返回值类型,设置为void也不行
3.创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次
4.构造方法可以重载
5.如果用户没有显式定义,编译器会自动生成一份默认的构造方法,生成的默认构造方法一定是无参的。一旦用户定义,编译器就不再生成。
6.构造方法中,可以通过this调用其他构造方法来简化代码,注意
(1).this()只能在构造方法里面调用构造方法,不能把this()放在一个普通的方法里面调用构造方法!
(2).构造方法当中,不能调用自己
(3).只能放在构造方法内部的第一行
(4).不能形成环,比如在第一个构造方法里面调用第二个,再在第二个构造方法里面调用第一个。

5.3默认初始化

在上文中提出的第二个问题:为什么局部变量在使用时必须要初始化,而成员变量可以不用呢?
先看个例子

class Student
{
    public int age;
    public String name;
    Student()
    {
        System.out.println("没有参数的构造方法!");
    }
    Student(int age,String name)
    {
        this.age=age;
        this.name=name;
    }
    public void print()
    {
        //this(18,"xiaobai");
        System.out.println("年龄:"+this.age+" "+"姓名:"+this.name);
    }
}
public class Class_And_Object2
{
    public static void main(String[] args)
    {
        Student student1=new Student();
        student1.print();
    }
}

这个代码运行的结果就是下面这个
在这里插入图片描述
原因是:成员变量在没有初始化的时候,都有一个默认值
最后一行的意思就是引用类型的默认值是null,比如String
类和对象--java(超级详细,两万字!)_第16张图片
下面这个图的意思就是student2不指向任何对象
在这里插入图片描述

5.4就地初始化

类和对象--java(超级详细,两万字!)_第17张图片
在一开始就给成员变量赋值,这种没什么意义,仅供了解

6.封装

6.1封装的概念

**面向对象程序三大特性:封装、继承、多态。**而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说就是套壳屏蔽细节。
比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器,USB插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU、显卡、内存等一些硬件元件。对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互

6.2访问限定符

在这里插入图片描述
看下面这个代码,原本String前面的权限是public(公开的),现在我改成了private(私有的),
第二个红色圈圈的name就变红了,也就是报错了,原因是我的权限改成了私有的之后,在类的外面就看不到这个name这个变量了,故也访问不到了
类和对象--java(超级详细,两万字!)_第18张图片
在这里插入图片描述
private就是在这里插入图片描述

类和对象--java(超级详细,两万字!)_第19张图片
default的意思就是什么都不加
比如
在这里插入图片描述
这两个成员变量前面既没有public,又没有private

public:可以理解为一个人的外貌特征,谁都可以看得到
private:只有自己知道,其他人都不知道
protected主要是用在继承中,继承部分详细介绍
default权限指:什么都不写时的默认权限
访问权限除了可以限定类中成员的可见性,也可以控制类的可见性

在这里插入图片描述

如上图,我们的age和name被private了,有人会问,你封装了之后,它们只能在类内使用呀,类外不能用啊,这该怎么办呢?
仔细想想,我们把细节隐藏起来了之后,我们要向外面提供公开的接口,这些公开的接口怎么实现呢?很简单,写几个方法就可以了!

class Student
{
    private int age;
    private String name;
    public void setName(String name)
    {
        this.name=name;
    }
    public String getName()
    {
        return this.name;
    }
    public void setAge(int age)
    {
        this.age=age;
    }
    public int getAge()
    {
        return this.age;
    }
    Student()
    {
        //System.out.println("没有参数的构造方法!");
    }
    Student(int age,String name)
    {
        this.age=age;
        this.name=name;
    }
    public void print()
    {
        System.out.println("年龄:"+this.age+" "+"姓名:"+this.name);
    }

}
public class Class_And_Object2
{
    public static void main(String[] args)
    {
        Student student=new Student();
        student.setAge(10);
        student.setName("小白");
        student.print();
    }
}

有人问了,这只有两个属性,设置getXXX和setXXX还比较方便,如果很多属性呢?
在IDEA里,我们可以这样
类和对象--java(超级详细,两万字!)_第20张图片
类和对象--java(超级详细,两万字!)_第21张图片
按住Shift将属性全选
在这里插入图片描述
就可以自动生成出来了

6.3封装扩展之包

6.3.1包的概念

在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。有点类似于目录。比如:为了更好的管理电脑中的歌曲,一种好的方式就是将相同属性的歌曲放在相同文件下。
在Java中也引入了包,包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。

6.3.2导入包中的类

Java 中已经提供了很多现成的类供我们使用. 例如Date类:可以使用 java.util.Date 导入 java.util 这个包中的 Date类.
类和对象--java(超级详细,两万字!)_第22张图片

当两个包底下都存在Date这个类的时候,编译器懵了
在这里插入图片描述
将第一个图和第二个图右边的代码块结合起来,最好不要用第二个图左边的代码的带*的形式
类和对象--java(超级详细,两万字!)_第23张图片
这个时候就要指定用哪个包的Date了
在这里插入图片描述

6.3.3自定义包

1.在文件的最上方加上一个 package 语句指定该代码在哪个包中.
2.包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.xiaobai.www )
3.包名要和代码路径相匹配. 例如创建 com.xiaobai.www 的包, 那么会存在一个对应的路径 com/xiaobai/www 来存储代码.
4.如果一个类没有 package 语句, 则该类被放到一个默认包中.
在IDEA中
5.包名要小写
类和对象--java(超级详细,两万字!)_第24张图片
一个叫com.xiaobai的包就建好了
在这里插入图片描述
再在包中创建类
在这里插入图片描述
类和对象--java(超级详细,两万字!)_第25张图片

6.3.4包的访问权限控制举例

类和对象--java(超级详细,两万字!)_第26张图片
可以看到,在com.xiaobai这个包下面创建了一个叫aaa的包和一个Testbai的类
类和对象--java(超级详细,两万字!)_第27张图片

通过上述代码将aaa包中的Testaaa类导入到了Testbai这个类里面了
import就是导入其他包当中的类

现在我们来说default,也就是什么限定符都不加
default它只能在同一个包里面访问
看下面两个图,我在Testaaa这个类里面定义了一个名字,然后将Testaaa这个类导入到Testaaa2这个类里面,然后在Testaaa2里面创建Testaaa的对象,然后输出testaaa.name这个名字,就是错的,因为Testaaa和Testaaa2分别在包aaa和包aaa2下面,属于两个包,如果在String name前面加一个public就不会错了
类和对象--java(超级详细,两万字!)_第28张图片
类和对象--java(超级详细,两万字!)_第29张图片

如果类全部在一个包底下
在这里插入图片描述
那么String前面啥也不加就不会错了
类和对象--java(超级详细,两万字!)_第30张图片
类和对象--java(超级详细,两万字!)_第31张图片

现在再回来看这个图(protected后面的博客会说到)
类和对象--java(超级详细,两万字!)_第32张图片

6.3.5常见的包

  1. java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。
  2. java.lang.reflect:java 反射编程包;
  3. java.net:进行网络编程开发包。
  4. java.sql:进行数据库开发的支持包。
  5. java.util:是java提供的工具程序包。(集合类等) 非常重要
  6. java.io:I/O编程开发包。

7.static成员

7.1再谈学生类

我们假设很多学生在一个教室上课,
所以我们要在每个对象的后面都加上这个教室的教室号
类和对象--java(超级详细,两万字!)_第33张图片
这三个333都占据了空间,但是因为他们都是一个班的学生,我们只想要一个333,故就有了static这个关键字
在这里插入图片描述
原本你的三个classroom都在堆区,这个时候只有一个classroom了,它就放到你的方法区了,这个时候我们就把classRoom叫做类变量(静态成员变量),不加static修饰的成员变量叫做普通成员变量
普通成员变量:每个对象都有一份
静态成员变量:所有对象共用一份
现在为止我们一共学习过以下三种变量
在这里插入图片描述

在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对
象,是所有对象所共享的。

7.2static(只能)修饰成员变量

static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共
享的。
【静态成员变量特性】

  1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
  2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
  3. 类变量存储在方法区当中
  4. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
class Student
{
    private int age;
    private String name;
    public static String classRoom="333";//教室就只有一份了
    public Student(int age, String name)
    {
        this.age = age;
        this.name = name;
    }
    public void print()
    {
        System.out.println("年龄:"+this.age+" "+"姓名:"+this.name);
    }
}
public class Class_And_Object2
{
    public static void main(String[] args)
    {
        Student student1=new Student(18,"小白");
        Student student2=new Student(19,"小红");
        Student student3=new Student(17,"小蓝");
        System.out.println("教室是:"+ Student.classRoom);//这两行都可以,推荐这一行,因为这个时候classroom这个成员变量已经不属于对象了,所以不推荐下面一行的形式
        System.out.println("教室是:"+ student1.classRoom);
    }
}

下面来看个题
类和对象--java(超级详细,两万字!)_第34张图片
答案是102
类和对象--java(超级详细,两万字!)_第35张图片
引用可以指向引用吗?
不可以!
在这里插入图片描述
类和对象--java(超级详细,两万字!)_第36张图片
在这里插入图片描述
下图student1其实只保存了最新的“zhangsan3”这个对象
在这里插入图片描述
在这里插入图片描述
下面这段代码的结果是333,因为classRoom已经不属于对象了

class Student
{
    private int age;
    private String name;
    public static String classRoom="333";//教室就只有一份了
    public Student(int age, String name)
    {
        this.age = age;
        this.name = name;
    }
    public void print()
    {
        System.out.println("年龄:"+this.age+" "+"姓名:"+this.name);
    }
}
public class Class_And_Object2
{
    public static void main(String[] args)
    {
        Student student1=null;
        System.out.println("教室是:"+ student1.classRoom);
    }
}

7.3static修饰成员方法

类和对象--java(超级详细,两万字!)_第37张图片
在这里插入图片描述
静态方法不依赖对象,所以不能在静态方法里面调用依赖对象的方法
类和对象--java(超级详细,两万字!)_第38张图片
因为print是依赖对象的,通过对象.引用来调用
而静态方法是通过类名.引用来调用的
这就是为什么在main方法里面调用别的方法的时候,别的方法要加上static

Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的。
【静态方法特性】

  1. 不属于某个具体的对象,是类方法
  2. 可以通过对象调用,也可以通过类名.静态方法名(…)方式调用,更推荐使用后者
  3. 不能在静态方法中访问任何非静态成员变量
  4. 静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用
  5. 不能用this.方法调用,因为this是调用当前对象的属性,然而这个方法已经不属于这个对象了
  6. 静态的方法里面只能用静态属性
  7. 在静态方法里面创建对象,调用print()方法就可以
  8. 静态方法无法重写,不能用来实现多态(此处大家暂时不用管,后序多态位置详细讲解)。

7.4static成员变量初始化

注意:静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性
静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化。

  1. 就地初始化
    就地初始化指的是:在定义时直接给出初始值

8.代码块

8.1代码块概念以及分类

类和对象--java(超级详细,两万字!)_第39张图片

8.2普通代码块

在这里插入图片描述

8.3构造(实例)代码块

构造块:定义在类中的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例成员变量
类和对象--java(超级详细,两万字!)_第40张图片

class Student {
    private int age;
    private String name;
    public Student(int age, String name)
    {
        this.age = age;
        this.name = name;
        System.out.println("带两个参数的构造方法");
    }

    //这个就是构造块,用来初始化实例成员变量的
    {
        this.name="小白";
        System.out.println("构造块");
    }
    
    public int getAge()
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
}
public class Class_And_Object4 {
    public static void main(String[] args)
    {
        Student student=new Student(20,"大白");
    }
}

运行结果如下
在这里插入图片描述
可以看到,构造块执行顺序在构造方法之前
这个时候,name被赋值成大白,因为构造方法比构造块运行顺序靠后

8.4静态代码块

使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
可以用静态代码块来给静态成员变量赋值

 private static String classroom;
    static
    {
        classroom="888";
    }

不能在静态代码块里面加上this.name类似的依赖对象的内容,因为静态的不依赖对象

class Student {
    private int age;
    private String name;
    private static String classroom;
    static
    {
        classroom="888";
        System.out.println("静态代码块");
    }
    public Student(int age, String name)
    {
        this.age = age;
        this.name = name;
        System.out.println("带两个参数的构造方法");
    }

    //这个就是构造块,用来初始化实例成员变量的
    {
        this.name="xiaobai";
        System.out.println("构造块");
    }

    public int getAge()
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
}
public class Class_And_Object4 {
    public static void main(String[] args)
    {
        Student student=new Student(20,"xiaobai");
    }
}

在这里插入图片描述
可以看到,静态块先执行,构造块其次,构造方法最后

这个意味着构造代码块和构造方法只会在实例化(创建)对象的时候才会被调用,而静态代码块意味着只要你的类被加载的时候就会被调用

class Student {
    private int age;
    private String name;
    public static String classroom;
    static
    {
        classroom="888";
        System.out.println("静态代码块");
    }
    public Student(int age, String name)
    {
        this.age = age;
        this.name = name;
        System.out.println("带两个参数的构造方法");
    }

    //这个就是构造块,用来初始化实例成员变量的
    {
        this.name="xiaobai";
        System.out.println("构造块");
    }
}
public class Class_And_Object4 {
    public static void main(String[] args)
    {
       // Student student=new Student(20,"xiaobai");
        System.out.println(Student.classroom);
    }
}

在这里插入图片描述

如果这样,静态代码块在静态成员变量前,这时候classroom被赋值203,这个时候是按住摆放顺序执行的
类和对象--java(超级详细,两万字!)_第41张图片
构造代码块也一样,这时候name就是88888,按照先后顺序
类和对象--java(超级详细,两万字!)_第42张图片

从书写习惯来说,先写成员变量,再写其他的

8.4.1静态代码块特性

1.静态代码块不管生成多少个对象,其只会执行一次

class Student {
    private int age;
    private String name;
    public static String classroom;
    static
    {
        classroom="888";
        System.out.println("静态代码块");
    }
    public Student(int age, String name)
    {
        this.age = age;
        this.name = name;
        System.out.println("带两个参数的构造方法");
    }

    //这个就是构造块,用来初始化实例成员变量的
    {
        this.name="xiaobai";
        System.out.println("构造块");
    }
}
public class Class_And_Object4 {
    public static void main(String[] args)
    {
        Student student1=new Student(20,"xiaobai");
        System.out.println("---------------------------------------");
        Student student2=new Student(21,"xiaobaibai");
    }
}

可以看到,即使再创建一个对象,但是第二个对象不会再调用静态代码块了
因为类只会被加载一次,所以静态的只会被执行一次
类和对象--java(超级详细,两万字!)_第43张图片

2.静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
3.如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)

//在编译器看来只有一个static,并且里面的内容会被合并
   static
    {
        classroom="888";
        System.out.println("静态代码块1");
    }
    static
    {
		System.out.println("静态代码块2");
    }
    static
    {
        System.out.println("静态代码块3");
    }

4.实例代码块只有在创建对象时才会执行
(上面已经举例了)
代码块一般不怎么用,考试的时候可能会用到

9.内部类

当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么这个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。

public class OutClass 
{
	class InnerClass
	{
	
	}
}
// OutClass是外部类
// InnerClass是内部类

9.1内部类的分类

9.1.1实例内部类

即未被static修饰的成员内部类。

public class OutClass
{
    public int data1=10;
    private int data2=20;
    private static int data3=30;
    class InClass
    {
        private int a;
        public int b;
        //private static int c;//实例内部类里面不能定义静态成员变量
        public void print()
        {
            System.out.println("实例内部类");
        }
    }
}

如何实例化 实例内部类对象
类和对象--java(超级详细,两万字!)_第44张图片
也可以访问外部类的变量

public class OutClass
{
    public int data1=10;
    private int data2=20;
    private static int data3=30;
    class InClass
    {
        private int data4=40;
        public int data5=50;
        //private static int c;//实例内部类里面不能定义静态成员变量
        public void print()
        {
            System.out.println(data1);
            System.out.println(data2);
            System.out.println(data3);
            System.out.println(data4);
            System.out.println(data5);
            System.out.println("实例内部类");
        }
    }
}

在内部类中不能定义静态的成员变量,因为被static修饰的变量不依赖对象,但是内部类依赖对象,怎么办呢?加一个final,因为final修饰的是常量,不是变量,常量是在编译的时候就已经确定了
类和对象--java(超级详细,两万字!)_第45张图片
如果在内部类再顶一个data1,print函数的运行结果的data1就是内部类的data1,不是外部类的data1,怎么访问外部类的data1呢
在这里插入图片描述
第一行就是外部类的data1的值,第二行就是内部类的data1的值
由此我们得出一个结论:实例内部类是存在外部类的this的

内部类和外部类同用一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件
【注意事项】

  1. 外部类中的任何成员都可以在实例内部类方法中直接访问
  2. 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
  3. 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名
    称.this.同名成员 来访问
  4. 实例内部类对象必须在先有外部类对象前提下才能创建
  5. 实例内部类的非静态方法中包含了一个指向外部类对象的引用
  6. 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。
    类和对象--java(超级详细,两万字!)_第46张图片

一般实例内部类不好,我们要定一个实例内部类对象的前提,还要定义一个外部类对象,故我们要定义一个实例内部类的对象,就要定义两个对象,那么我们可不可以只定义一个对象呢?当然可以,下面就是我们的静态内部类

9.1.2静态内部类

1.如何实例化静态内部类对象
类和对象--java(超级详细,两万字!)_第47张图片

类和对象--java(超级详细,两万字!)_第48张图片
OuterClass2.InnerClass2()是一个整体
2.如何在内部类当中访问外部类的非静态类成员
类和对象--java(超级详细,两万字!)_第49张图片
定义一个外部类对象

public class OutClass
{
    public int data1=10;
    public int data2=20;
    public int data3=30;
    static class InnerClass
    {
        public int data4=40;
        public int data5=50;
        public void print()
        {
            OutClass outClass=new OutClass();
            System.out.println(outClass.data1);
            System.out.println(outClass.data2);
            System.out.println(outClass.data3);
            System.out.println(data4);
            System.out.println(data5);
        }
    }

    public static void main(String[] args)
    {
        OutClass.InnerClass innerClass=new OutClass.InnerClass();
        innerClass.print();
    }
}

9.1.3局部内部类

定义在方法里边的类,该种内部类只能在其定义的位置使用,一般使用的非常少,此处简单了解下语法格式。
只能在对应的方法里面实例化对象

public class OutClass
{
    public void method()
    {
        class InnerClass
        {
            public int data=10;
        }
        InnerClass innerClass=new InnerClass();
    }
    
}

【注意事项】

  1. 局部内部类只能在所定义的方法体内部使用
  2. 不能被public、static等修饰符修饰
  3. 编译器也有自己独立的字节码文件,命名格式:外部类名字$数字内部类名字.class
  4. 几乎不会使用

9.1.4匿名内部类

interface Shape
{
    void draw();
}
public class OutClass
{
    public static void main(String[] args)
    {
    //类名叫啥我不太清楚--匿名内部类
       new Shape(){
           public void draw()
           {
               
           }
       };
    }
}

类和对象--java(超级详细,两万字!)_第50张图片
类和对象--java(超级详细,两万字!)_第51张图片

10.对象的打印

当我们实例化一个对象的时候,打印对象名称打印出来的是地址
类和对象--java(超级详细,两万字!)_第52张图片
这个地址是通过println来打印出来的
println的底层逻辑是这样的
类和对象--java(超级详细,两万字!)_第53张图片
我们可以看到,最终的println是通过tostring这个方法实现的,那么我们可不可以自己写一个toString方法呢?
当然可以
我们让我们自己写的toString这个方法的名字和最终println调用的toString这个方法的名字一模一样

class Student 
{
    public String toString()
    {
       return "小白";
    }
}
public class Class_And_Object4 {
    public static void main(String[] args)
    {
        Student student=new Student();
        System.out.println(student);
    }
}

这个时候输出student这个对象的时候就不是地址了,是我们自己的toString方法的返回值
类和对象--java(超级详细,两万字!)_第54张图片

你可能感兴趣的:(java,开发语言)