类和对象(Java详解)

目录

一.对象

1.什么是对象

2.什么是面向对象

二.类

1.什么是类?

如何定义类?

2.如何使用类?

3.对类和对象的总结有哪些?

三.this

1.this关键字

2.使用this关键字的注意点

四.构造方法

1.构造方法的概念

2.构造方法到底能干个啥

3.构造方法需要注意的事项

五.封装

1.java中是如何实现封装这样的操作者的?

六.static成员

1.再谈学生类

2.static 修饰的成员变量 或 成员方法 以及成员变量初始化

3.静态成员,静态方法的特性

七.代码块

1.代码块中需要注意的事项

八.内部类

1.内部类是什么?

2.成员内部类的特点

3.静态内部类的特点(被static修饰的内部类)

4.局部内部类的特点(定义在类方法内部的)

5.匿名内部类的特点(没有名字的类,本质还是个类)(重点)

1.我只想单纯的使用一次eat()方法,不需要创建对象,我们就可以用到匿名内部类

2.如果我们想要调用多个方法,我们可以用以下方式访问

3.我们匿名内部类最常见的用法是通过实参传递调用

九.对象的打印



一.对象

1.什么是对象

万物皆对象,只要是描述一个具体且客观存在的事物都能被称为对象,比如一栋房子,一辆车子,一块橡皮。是不是很神奇,没想到我们身边会有这么多对象吧!所以在学完本章节后不要说自己没有对象哦!在我们学习类和对象之前让我们先来认识一下java中的面向对象和C语言中的面向过程有什么不同的地方吧。

面向对象的初步认知!

2.什么是面向对象

java就是一门非常纯粹的面向对象编程语言(又被称为Object Oriedted Program,简称OOP),在面向对象的世界里,一切皆为对象。并且面向对象是解决问题的一种思想,问题的解决是通过对象之间的交互完成,接下来就让我们探讨一下面向对象和面向过程的区别。

面向过程和面向对象之间的区别。

面向过程在解决问题的时候是把问题分析出很多步骤,然后用函数一步一步的去实现,再调用的时候,在一个一个去调用就行。而面向对象在解决问题的时候是把问题分解成每一个对象,建立对象是为了描述某个事物在整体解决问题的步骤中的行为,而不是为了完成一个步骤。

例如:

我们用烧菜举例,我们用面向过程的思想来解决是这样做:如果是在家烧菜,我们首先需要去买菜,其次洗菜,然后烧菜,最后装盘吃菜,我们只有一步一步的去完成相对应的步骤后,最终才能实现想要的结果。而我们的面向对象的思想又是怎样的呢:当我们去烧菜馆吃菜的时候,我们只用去点菜,然后等菜上来直接吃就行,这样的做法就是面向对象的思想,就更像是一种功能,不用去实现好多步骤,我们想用的时候直接去使用就行,而面向过程是需要我们一步一步的去实现解决问题的步骤。这就是面向对象和面向过程之间的区别。

二.类

1.什么是类?

我们在刚刚的学习中知道了什么是对象,而类就是用来描述一个对象的,你可以把类当做一个关于某个实体对象的容器,这个容器里面装着的都是对某个实体对象有哪些属性和功能的描述,等描述完之后就可以用计算机去识别这个对象,这就是类!

如何定义类?

首先定义一个类的时候是需要使用到class关键字在类名之前进行修饰的。

格式:修饰符 class 类名称{ }

而在{ }这个大括号是用来包含类的主体的,就是包含着描述某个实体对象的一些属性和方法的,也可以把他们叫做类成员,类方法。

属性定义格式:【修饰符】属性类型 属性名 = 【初始值】;

学者可能会有疑虑,属性到底是啥?属性就是用来定义这个类对象的中包含的一些数据和某些静态特征的,可以认为属性就是我们之前所学的变量,只是换了一个地方。

方法的定义格式:【修饰符】方法返回值类型 方法名(形参列表){}

方法就是用来实现某个对象的某些特有功能的一个主体!

代码展示:

class Student{
    public Stirng name;
    public int age;
    
    public void draw(){
        System.out.println("画画");    
    }
}

注意:

1.在定义类名的时候推荐使用大驼峰(首字母大写)格式定义。

2.在属性和方法前修饰符暂时统一为public,并且类中写的方法暂时不带static关键字,后面的学习中会解释到。

3.void 表示无返回值(方法里面暂时统一用void返回值类型便于理解)。

4.类名称不能用java中的关键字。

2.如何使用类?

        通过前面的学习,我们知道了类是干啥的,类有什么作用!但我们不会用那不就白学了嘛!所以接下来我们就来学习类是如何使用的:

        在调用类之前首先要实例化一个对象,才能对类中属性和方法进行调用!此时学者就会有问题了,什么是实例,什么又是实例化?为什么要实例化才能调用这个类成员呢!相信大家在这也是一头雾水!下面我也给出了详细的解释:

        咱们现在都知道了什么是对象,而这个实例是可以称为一个对象,一个对象也叫做一个实例,所以在这实例就是对象,对象就是实例。哪什么又是实例化呢?我举个例子,将一个int看成一个类,它的变量a看成类的对象,当在声明变量的过程的时候就叫做实例化。因为类是自定义类型,在调用类里面的方法和属性的时候是需要用类去实例化一个类的对象的,你可以理解为就像在c语言里面去调用一个方法的时候需要提前去声明一下这个方法的,要不然就不能使用!这就是为什么要实例化这个对象的原因。

        大家现在应该对实例化有了一个简单的认识,那么新的问题又来了!如何创建一个类的对象了?哈哈,不知道吧!

        在实例化一个对象的时候,我们是需要new关键字配合类名一起使用的。

        实例化对象格式:类名 变量名 = new 类名(参数列表)注意:这个参数列表咱们先不用管它!

        创建好了一个实例化对象,那么我们又该用怎样的方式去调用类中的成员属性和成员方法了。

        调用属性格式:变量名.成员属性

        调用方法格式: 变量名.成员方法

        没错中间的符号就是 点 符号,就是调用属性和方法的关键所在,记住了嘛!嘿嘿!

代码展示:

class Student{
    public String name;
    public int age;
    
    public void Dog(){
        System.out.println("狗叫什么");    
    }
}
public class Test{
    public static void main(String[] args){
        Student student = new Student();
        student.name = "张三";
        student.age = 14;
        System.out.print(student.name+student.Dog()));    
    }
}

        注意:上面的代码中Student类前面没有public修饰是因为每个类中只能有一个公共类,而这个公共类需要与文件名一致,在工作中一般一个文件只写一个类,在这里只是学习所以不做过多解释。其次有没有发现,类实例化出了两个对象,说明类是可以创建多个实例的。

3.对类和对象的总结有哪些?

        1.类就像是用来描述一个实体事物(对象)的模型。

        2.类是一种自定义的类型,是可以用来定义变量的。

        3.一个类是可以实例多个对象,实例化出的对象是占用实际物理空间的,存储类成员变量的。

        4.在调用类中的成员属性和成员方法和需要通过实例化出的对象进行调用的。只有实例化出的对象才能存储数据和占用物理空间的。

三.this

1.this关键字

这个this关键字到底有啥作用,我们为什么要使用这个this关键字,下面我来观察一段代码:

class Student{
    public String name;
    public int age;
    
    public void SetName(String name,int age){
        name = name;
        age = age;    
    }
    public void print(){
        System.out.println("名字:"+name +"年龄:"+age);    
    }
}
public class Teset{
    public static void main(String args[]){
        Student student = new Student();
        student.SetName("bit",18);    
    }
}

        此时是不是就会纳闷,为什么五六行代码的变量名都是一样的,那么我们这个地方到底是局部变量赋值给成员变量还是成员变量赋值给局部变量了,让我们傻傻分不清!这个时候this的作用就来,他就是用来区分局部变量和成员变量同名时 那个是成员变量 那个又是局部变量的,下面给出了详细代码参考:

class Student{
    public String name;
    public int age;
    
    public void SetName(Student this,String name,int age){
        this.name = name;
        this.age = age;    
    }
    public void print(){
        System.out.println("名字:"+name +"年龄:"+age);    
    }
}

        哈哈,是不是觉得很奇怪,这样我们就能区分成员变量和局部变量了!对 就是这样!"this."在谁前面一般那个就是成员变量,而后面赋值的就是局部变量,而我们发现在SetName()方法中,还声明了一个Student对象的变量名this,那我们下面的关键字this是不是跟这个this是同一个了,对,就是同一个,在平时书写代码的时候我们可以不用写这个Student对象的this变量,因为this关键字在java中是一个隐藏类型的关键字,我们是可以直接拿来使用的,在这个地方写出来,是为了便于我们理解。

        我们刚刚用this区分好了成员变量和局部变量,那么我们新的问题又来了,如果有两个不同的对象调用Student对象中的print()方法,那么我们该如何判断print方法输出的是哪个对象的变量名了:

class Student{
    public String name;
    public int age;
    
    public void SetName(String name,int age){
        name = name;
        age = age;    
    }
    public void print(){
        System.out.println("名字:"+this.name +"年龄:"+this.age);    
    }
}
public class Teset{
    public static void main(String args[]){
        Student student1 = new Student();
        student1.SetName("bit",18);
        student1.print();   
        Student student2 = new Student();
        student2.SetName("比特",19);
        student2.print();
    }
}

        我们这个地方可以有两个方法来判断,一个是 在主方法main中,“.”前面的是哪个对象名,那么调用并输出的就是那个对象。还有一个方法就是看this,那个对象名调用这个方法,这个this此时就代表着这个对象。所以,我们能得出一个结论,就是this指向的是当前引用的对象,在成员方法中所有的操作都是靠引用访问完成的。

2.使用this关键字的注意点

        1.this关键字只能在成员方法中使用

        2.this关键字是不需要咱们声明定义的,它是java的隐藏类型的关键字

        3.this的类型:this是对应

        4.在构造方法中调用this关键字(下面的构造方法中会详细提到)

四.构造方法

        我们有没有想过这样一个问题,就是在一个类中或者是一个方法中,成员变量都不需要初始化,而局部变量就都必须初始化,局部变量不初始化就会报错?

        我刚学java的时候也很纳闷,也在想,到底为什么啊,直到我了解到了jvm层面的时候,我知道了为什么成员变量就不需要初始化了!

        因为在jvm层面的,我们在new一个对象的时候,它会自动的去检查对象中有哪些属性,并且在检查的同时会帮它们分配内存空间,当我们没有初始化对象中的属性的时候,我们的jvm层面会自动为我们设定计算机中设定好的初始值,有人会问,为啥局部变量就他就不能自动设置一个初始值了,哈哈,这个你就得问问当时创造这门语言的老大了,是他没有给局部变量在语言中设置这样一个东西,所以,咱们到现在的学习过程中,就要手动的给他局部变量赋值。现在是否清晰一丢丢,为啥我要给局部变量初始化啊!初始化所分配的空间图:

数据类型

默认值

byte

0

char

'\u0000'

short

0

int

0

long

0L

boolean

false

float

0.0f

double

0.0

reference

null

1.构造方法的概念

        构造方法它是一个特殊的成员方法,名字必须和类名一致,一般使用public 修饰,并且不能有任何的返回类型,void也不行,当然它最大的特点就是,在创建一个对象的时候,它是由编译器自动调用并且在整个对象的周期内只会被提调用一次。

2.构造方法到底能干个啥

        构造方法的出现是为了更加方便的初始化我们的成员变量,咱们可以在下面代码体会一下:

class Student{
    public String name;
    public int age;
    
    public Student(String name,int age){
            this.name = name;
            this.age = age;
    }
    public void SetName(String name,int age){
        this.name = name;
        this.age = age;    
    }
    public void print(){
        System.out.println("名字:"name+"年龄:"+age);    
    }
    
}
public class Test{
    public static void main(String[] args){
        Student student1 = new Student();
        student1.name = "三";
        student2.age = 15;
        student1.SetName("张三",14);
        student1.print();
        Student student2 = new Student("李四",15);
        student2.print();
            
    }
}

        大家是不是发现了在上述代码中,有一个和类名同名的方法,没错他就是我们今天要学的构造方法,我们再仔细审阅会发现,咦,这个构造方法里面是不是定义了两个和成员变量相同的方法名,很纳闷是把,这是在干啥,我们把目光转移到main()方法里面,我们看到了什么,是不是在第二个对象中直接在实例化对象的时候进行了传参赋值,没错,所以我们上面的那个定义的构造方法就是用来接收我们的传参值,对我们的类中的成员变量进行初始化的,为什么说构造方法能够更加方便的初始化我们的内容了,大家看,我在Student对象中是不是还定义了一个SetName方法,再看我们的主方法main()里面我们如果想给成员变量初始化值要么得通过对象调用这个属性进行赋值,要么通过调用SetName()方法进行传参赋值,再看看我们的构造方法,直接在对象实例化的时候就进行传参赋值,不服咱直接干就完了 哈哈哈,现在有没有觉得我们的构造方法对初始化更加方便呢!

        哈哈,告诉你们一个小秘密,咱们学习的构造方法其实也是一个隐藏类型的方法,没想到吧哈哈哈哈,那他是不是可以像之前的this方法一样,直接用,不用重新定义呢!在java中是不行的哦,就是在类中,当你没有定义一个构造方法的时候,系统是会自动给你默认一个无参的构造方法的:

class Student{
    public String name;
    public int age;
    
    public Student(){
        //这个里面其实啥也没有
        System.out.println("系统给的默认构造方法");    
    }
    public Student(String name,int age){
        this.name = name;
        this.age = age;    
        System.out.println("咱们自己定义的构造方法");
    }
}
public class Test{
    public static void main(String[] args){
        Student student = new Student();
        Student student2 = new Student("jason",14);                
    }
}

        咱们看,上面的代码中就很清晰的展示了我们默认的构造方法和自定义的构造方法,当我们在没有给出自定义的构造方法的时候就会调用咱们系统默认的构造方法,当我们给出了自定义的构造方法,并且在主方法中进行了对成员属性初始化的时候,咱们就会调用我们自定义的构造方法,切记,当我们自定义构造方法之后 系统就不会在给无参的构造方法了。说了半天这个构造方法,相信大家会有一个疑问,就是这个构造方法啊,它是在哪个地方,哪个时刻生效的,大家还是看上面的代码,在main()主方法的时候我们的构造方法是在我们创建对象,实例化对象的时候开始生效的,开始起作用的,是不是豁然开朗了的感觉!对了,差点把这茬给搞忘了,我们再次观察图中的代码,我们有没有发现,在Student类中我们定义了两个Student的构造方法,但是不会报错,为什么,因为我们的构造方法是可以进行重载的,只要类名相同,参数不同就可以进行重载。怎么样构造方法是不是很神奇!

        一直在说构造方法的使用情况和使用特点,那么我们还有没有在构造方法中更加简便的初始化方法了,还真有,来咱们继续欣赏以下代码:

class Student{
    public String name;
    public int age;
    
    public Studnet(){
        this("zhangsan",13);    
    }
    public Student(String name,int age){
        this();//这个地方一定会报错
    }
}

        是不是没想到还可以这样,当我们在自定义构造方法的时候,不想定义局部变量进行传参的时候,我们是可以直接在构造方法里面通过调用this()关键字直接给它进行传参赋值的,看我们的第一个构造方法就是这样的,那为什么我们在第二个构造方法里面会出现报错的效果了,因为java语言中,使用this关键字是不能出现闭环效果的,而在这个地方我们就出现了相互调用,形成闭环的问题,导致报错!如果只是需要静态类型的传参我们是可以这样实现的,如果要实现动态类型的传参,我们还是建议通过实例化来进行调用。我们在构造方法中使用this关键字特别注意的一点是,它必须放到我们的第一条语句,必须放到第一条语句,放到第一条语句(重要的事情说三遍),还有就是,我们只能在构造方法中这样使用关键字,其他的地方不行!

3.构造方法需要注意的事项

        学习了构造方法的使用情况和语言特点,接下来让我们一起看看,咱们在使用构造方法的时候需要注意的事项有哪些把!

        1.在进行定义构造方法的时候,名字必须和类名一致,并且不能有任何返回值类型,void也不行(重点)

        2.在构造方法中使用this关键字进行赋值的时候,必须把this语句放在第一条语句,切记,不要形成闭环,且只能在构造方法中使用this关键字

        3.构造方法是可以重载的,可以根据咱们自己的需求进行调整内容

        4.构造方法是在咱们创建对象的时候,自动编译并调用的,并且他只能被调用一次,在整个程序运行周期内

        5.当一个类中没有定义任何构造方法的时候,编译器会默认生成一个无参构造方法,理论上是可以通过编译的,但是实际上会出现编译报错。

        6.虽然在其他方法中调用this()使方法形成闭环,但是可以在构造方法中通过this调用其他构造方法来简化代码。如下所示:

public class Date {
    public int year;
    public int month;
    public int day;

    public Date(){
        //没有下面的有参构造方法,这个地方一定报错
        this(1999,2,3);
    }

    public Date(int year,int month,int day){
        this.year = year;
        this.month = month;
        this.day = day;
        //this();会形成闭环
    }
}

五.封装

        我之前常常把Java中的三大特性和面向对象的三大特性视为一种说法,在这纠正一下没有Java的三大特性这一说法,只有面向对象的三大特性分别是:封装,继承,多态。那么在这一小节我们主要了解封装的作用?大家可以思考一下封装有什么作用?

        在科技发达的今天,电脑已然成为家庭不可或缺的办公用品,像电脑这样一个复杂的设备,只提供给用户一些关开机,键盘,显示器,USB接口等,我们就能使用这样一台电脑。而电脑中实际工作却是cpu,显卡,内存等一些软硬件......计算机的使用者是不需要知道这些电脑的内部如何去运作的,会使用键盘和鼠标与计算机进行交互即可,所以电脑在出厂的时候会把电脑用外壳包装一下,仅仅对外提供关开机,鼠标,键盘等,让用户与计算机交互即可。

        封装他就是这样,在Java中它让数据和数据中的方法进行有机的结合,进对外公开接口和对象 与外面的其他类进行交互(接口后序会详解)。

1.java中是如何实现封装这样的操作者的?

        是什么能将类和数据以及数据中的方法结合在一起的呢,大家可曾听闻访问限定符,没错就是访问限定符来实现类与数据及数据方法的封装的,这个访问权限就可以用来控制方法或者字段能否直接在类外使用。那么我们java中提供了四种访问限定符:

        1.private.(在一个类中,只能够仅在自己的类中访问)

        2.default.(在同一个包中,不同类都可以访问)

        3.protected.(在不同的包中的子类中都可以经常访问,主要用于继承中)

        4.public.(同一包中,不同包中,只要是在这个项目范围内都能被访问到)

class Computer{
    private String cpu;
    private String memory;
    public String screen;
    String brand;//默认default属性

    public Computer(String cpu, String memory, String screen, String brand) {
        this.cpu = cpu;
        this.memory = memory;
        this.screen = screen;
        this.brand = brand;
    }

    public void Boot(){
        System.out.println("开机");
    }
    public void PowerOff(){
        System.out.println("关机");
    }
    public void SurfInternet(){
        System.out.println("上网");
    }

}
public class TestComputer {

    public static void main(String[] args) {
        Computer computer = new Computer("hw","i1","8G","13*14");
        System.out.println(computer.brand);//default:只能被本包中的类访问
        System.out.println(computer.screen);//public:可以被任何其他类访问
        //System.out.println(computer.cpu);//private:只能在Computer类中访问
    }
}

六.static成员

1.再谈学生类

class Student{
    public String name;
    public String classRoom;//班级
    
    public double score;//成绩

    public Student(String name, String classRoom, double score) {
        this.name = name;
        this.classRoom = classRoom;
        this.score = score;
    }
}
public class Test {

    public static void main(String[] args) {
        Student s1 = new Student("xioaxiao","1班",99);
        Student s2 = new Student("mingming","1班",78);
        Student s3 = new Student("honghong","1班",98);
        
    }
}

        像上面这样一段代码,描述的是学生的姓名,班级,成绩,通过代码我们发现,这几位同学都是一个班级的,所以课程表都是一样的,那么我们在Java中有没有这样的一个方法或者成员能够表示这个课程表是他们所共有的,不需要我们对每个同学都给一份。

        有的,被static修饰的成员(也称为静态成员或者类成员),就能起到这样的一个作用,其不属于某个具体的对象,是所有对象所共享的。

2.static 修饰的成员变量 或 成员方法 以及成员变量初始化

class Student{
    public String name;
    public String classRoom;//班级

    public double score;//成绩

    public static String time = "课程表";

    public Student(String name, String classRoom, double score) {
        this.name = name;
        this.classRoom = classRoom;
        this.score = score;
    }
}
public class Test {

    public static void main(String[] args) {
        Student s1 = new Student("xioaxiao","1班",99);
        Student s2 = new Student("mingming","1班",78);
        Student s3 = new Student("honghong","1班",98);

        //静态成员变量可以通过类名直接访问(推荐)
        System.out.println(Student.time);

        //也可以通过对象名来进行访问
        System.out.println(s1.time);
        System.out.println(s2.time);
        System.out.println(s3.time);

    }
}

        在上述展示代码中可以看到静态成员最大的特性:不属于某个具体的对象,是所有对象所共享的。

        在类中我们的方法,一般是由private修饰 通过对象调用方法来访问,而public修饰的可以直接通过对象访问,那么我们在类中被static修饰的成员方法该如何进行访问呢!

class Student{
    public String name;
    public String classRoom;//班级

    public double score;//成绩

    public static String time = "课程表";

    public Student(String name, String classRoom, double score) {
        this.name = name;
        this.classRoom = classRoom;
        this.score = score;
    }

    public static void methondA(){
        //静态方法中调用静态成员变量
        System.out.println(Student.time);
        //静态方法中无法调用非静态成员变量 why?
       // System.out.println(this.score);
        //静态方法中无法调用非静态成员方法 why?
        //methondB();
    }
    public void methondB(){
        //非静态方法中调用静态成员变量
        System.out.println(Student.time);
    }
}
public class Test {

    public static void main(String[] args) {
        Student s1 = new Student("xioaxiao","1班",99);
        //静态成员变量可以通过类名直接访问(推荐)
        System.out.println(Student.time);

        //也可以通过对象名来进行访问
        System.out.println(s1.time);

        上述例子中我们会发现,被static修饰的方法是类中的一种方法,不被某个对象所持有,并且静态成员一般是由静态方法所访问的。至于为什么静态方法中不能访问非静态成员是因为在非静态方法被调用时会有this参数在其中传递信息,而静态方法中无法传递this引用。

3.静态成员,静态方法的特性

        1.生命周期伴随类的一生(随类的加载二创建,随类的卸载而销毁)

        2.类变量存储在方法区中(不存储在某个对象的空间中)

        3.访问方式:类名.静态成员(推荐),对象名.静态成员

        4.它属于类的一种属性,不属于某个具体的对象,是对所有对象共享的

        5.静态成员属于类方法,不能在静态方法中访问非静态成员

思考:静态成员变量既然是类属性,那么在未初始化前能否自动初始化或放在构造方法中进行初始化?

静态成员变量的初始化分为两种:就地初始化或者静态代码块初始化。

所谓的就地初始化就是在声明定义静态成员变量时,直接给定初始值。

public static String time = "课程表";

静态代码块初始化(马上详解)被static所定义修饰的代码块叫静态代码块,它的作用一般用于初始化静态成员变量。

public static String A;
//静态代码块
static{
    A = "classRoom";
    System.out.println(A);
}

七.代码块

        什么是代码块?有哪几种代码块?以及常用的代码块那些?代码块在代码中有没有先后执行顺序?

        被{}括号括起来的称为代码块,根据代码块定义的位置以及关键字可以分为四种,分别是:普通代码块,构造块,静态块,同步代码块(多线程再讲)。

用的比较多的是静态代码块和同步代码块,而同步代码块在多线程中最多。

class Person{
    private String name;
    private String gender;
    private int age;

    private static String classRoom;

    //静态代码块
    static {
        classRoom = "bit";
        System.out.println("我是一个静态代码块");
    }
    public Person(){
        System.out.println("我是普通代码块");
    }

    //实例代码块
    {
        this.name = "bit";
        this.age = 12;
        this.gender = "man";
        System.out.println("我是实例代码块");
    }

    public void show(){
        System.out.println("name:" + name + "gender:" + gender + "age:" + age);
    }
}
public class Code {

    public static void main(String[] args) {
        //不创建对象无法执行实例代码块,好好想想为啥?
        Person person1 = new Person();
        System.out.println("==================");
        Person person2 = new Person();
    }
}

此代码展示了代码块在实际中如何应用的,以及三种不同的代码块的执行顺序。

1.代码块中需要注意的事项

        1.是实际执行中,静态代码块最先执行,实际代码块其后,普通代码块最后。静态代码块在整个生命周期只会执行一次,而实例代码块只有在创建对象时才会被执行

        2.类中包含多个静态代码块,编译器会按照定义的先后次序依次执行

        3.静态成员变量是类的属性,它会在jvm加载类时,开辟空间初始化

八.内部类

1.内部类是什么?

        从字面意思上理解,就是一个类写在另一个类的内部!就像嵌套一样,一个完整的类嵌套着另一个完整的类结构,这个被嵌套的类就被称为内部类,而嵌套着它的类就做外部类。

        内部类一般包含四种:成员内部类,静态内部类,局部内部类,以及匿名内部类。

        成员内部类(又称实例内部类)

2.成员内部类的特点

        1.内部类可以直接访问外部类的任何成员,内部类与外部类成员享有共同的限定符约束

        2.外部类不能直接访问内部类的成员,必须要创建内部类对象进行访问

        3.实例内部类被访问必须先有外部类的对象的前提下创建并且实例内部类的非静态方法中包含一个指向外部类对象的引用

        4.实例内部类方法中访问外部类同名成员时,优先访问自己的,如果要想访问外部类的同名成员:外部类名.this.同名成员

样式代码演示:

public class OutClass{

    private int count;
    static int sum;
    int c;
    public void MethondA(){
        count  = 20;
        System.out.println(count);
    }

    public void MethondB(){
        sum = 10;
        System.out.println(sum);
    }
    class InnerClass{
        int c;

        //实例内部类
        public void MethondInner(){
            //在实例内部类中可以直接访问外部类的中的成员(private和static修饰)
            count = 30;
            sum = 20;
            MethondA();
            MethondB();
            //如果访问的内部类成员与外部类成员同名,优先访问自己的
            c = 100;
            System.out.println(c);
            //访问外部类同名成员
            //外部类名.this.同名成员
            OutClass.this.c = 200;
            System.out.println(OutClass.this.c);
        }
    }

    public static void main(String[] args) {
        //外部类访问
        OutClass outClass = new OutClass();
        System.out.println(outClass.count);
        System.out.println(OutClass.sum);
        outClass.MethondA();
        outClass.MethondB();

        System.out.println("实例内部类访问");
        //在进行实例内部类访问时,首先要创建外部类对象
        //因为内部类是包含在外部类里面的,访问内部类成员必须借助外部类
        //创建实例内部类对象
        //第一种实现内部类对象
        OutClass.InnerClass innerClass1 = new OutClass().new InnerClass();
        //第二种实现内部类对象
        OutClass.InnerClass innerClass2 = outClass.new InnerClass();
        innerClass2.MethondInner();

    }
}

3.静态内部类的特点(被static修饰的内部类)

        1.静态内部类是不需要依赖外部类的,和类的静态成员属性类似,并且在静态内部类中不能使用非静态成员变量或方法

        2.虽然不能访问非静态成员变量或方法,但是静态内部类中能声明定义非静态成员变量或方法

样式代码演示:

class OutClass{
    private int count;
    static int sum;

    public void MethondA(){
        count = 10;
        System.out.println(count);
    }
    public static void MethondB(){
        sum = 20;
        System.out.println(sum);
    }

    //静态内部类(被static修饰的内部类)
    static class InnerClass{
        public void MethondInner(){
            //既然是静态内部类也就是和静态成员有着同样的属性
            //在静态内部类中只能访问外部类的静态成员
            //当然静态内部类中虽然只能访问外部类的静态成员
            //但是静态内部类中能声明静态成员也能声明非静态成员
            sum = 200;
            System.out.println(sum);
            MethondB();
            //MethondA();编译失败


        }
        public int tmp = 10;
        public void MethondA(){
            System.out.println(tmp);
        }

        }
        public static void main(String[] args) {
            //静态内部类实例对象
            OutClass.InnerClass innerClass =  new OutClass.InnerClass();
            innerClass.MethondInner();
    }
}

4.局部内部类的特点(定义在类方法内部的)

        1.定义在类方法内部的局部类,并且只能在这一个方法内部使用

        2.不能被public,static修饰

        3.能直接访问方法中的属性和方法,也能直接访问外部类中属性和方法

样式代码演示:

class OutClass2{
    private int count;
    static int sum;

    public void MethondA(){
        count = 20;
        sum =10;
        //局部内部类(定义在方法内部不能被static和public 修饰)
        class InnerClass{
            public void MethondInner(){
                System.out.println(count);
                System.out.println(sum);
            }
        }
        //只能在方法内部使用
        InnerClass innerClass = new InnerClass();
        innerClass.MethondInner();

    }

}

5.匿名内部类的特点(没有名字的类,本质还是个类)(重点)

Java匿名内部类(参考)

        1.使用匿名内部类时,我们需要去继承一个或者实现一个接口,两者二选一,并且必须实现继承的类或者实现的接口中的所有的抽象方法(匿名内部类不能是抽象的)

        2.其实匿名内部类也属于局部内部类,所以局部内部类里面的限制对匿名内部类同样生效

        3.匿名内部类不能定义构造函数,不能存在任何静态成员变量或静态成员方法

 匿名内部类使用场景

1.我只想单纯的使用一次eat()方法,不需要创建对象,我们就可以用到匿名内部类

样式代码展示:

interface A{
    void eat();
}
class B implements A{
    @Override
    public void eat() {
        System.out.println("我要吃饭");
    }
}

public class Test{
    //匿名内部类方式
    public static void main1(String[] args) {
    new A(){
        @Override
        public void eat() {
            System.out.println("我要吃饭");
        }

    }.eat();
}
//传统方式
    public static void main(String[] args) {
        B b = new B();
        b.eat();
    }
}

2.如果我们想要调用多个方法,我们可以用以下方式访问

interface A{
    void eat();
    void sleep();
}
class B implements A{
    @Override
    public void eat() {
        System.out.println("我要吃饭");
    }

    @Override
    public void sleep() {
        System.out.println("dark");
    }
}

public class Test{
    public static void main(String[] args) {
    A a = new A() {
        @Override
        public void eat() {
            System.out.println("我要吃饭");
        }

        @Override
        public void sleep() {
            System.out.println("我要睡觉");
        }
    };
        a.eat();
        a.sleep();
        //获取匿名内部类的名字
        System.out.println(a.getClass());
    }
}

3.我们匿名内部类最常见的用法是通过实参传递调用

interface A{
    void eat();
    void sleep();
}
class B implements A{
    @Override
    public void eat() {
        System.out.println("我要吃饭");
    }

    @Override
    public void sleep() {
        System.out.println("dark");
    }
}

public class Test{
    public static void f(A a){
        a.eat();
    }
    public static void main(String[] args) {
        f(new A() {
            @Override
            public void eat() {
                System.out.println("没有创建对象,也没有实现接口就能调用f方法");
            }
            @Override
            public void sleep() {
            }
        });
    }
}

以上便是四种不同内部类的用法和特点。

九.对象的打印

我们先看一段代码:

class Person1{
    String name;
    String gender;
    int age;

    public Person1(String name, String gender, int age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }

   
}
public class Test5 {
    public static void main(String[] args) {
        Person1 person = new Person1("Jim","男",12);
        System.out.println(person);
    }
}

我们可以从结果看到,我们打印的对象是一串地址符,那么现在我们需要看到的是对象中名字,年龄,性别这样的打印效果:

我们该如何实现了?我们在Person这个类里面重写toString这个方法就能打印上面的效果!!

class Person{
    String name;
    String gender;
    int age;

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test5 {
    public static void main(String[] args) {
        Person person = new Person("Jim","男",12);
        System.out.println(person);
    }
}

好了,类和对象这一章节就学到这里了,上述有任何错误,内容不全,希望各位码友积极在评论区指出。

  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值