Java——类和对象

一、面向对象的初步认识

1.1 什么是面向对象

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

1.2 面向对象与面向过程

  • 1.传统洗衣流程(面向过程)

拿盆>放水>放衣服>放洗衣粉>手搓>换水>放洗衣粉>手搓>换水>拧干>晾衣服
传统的方式注重的是洗衣服的过程,少了一个环节可能都不行,而且不同类型衣服洗的方式、拧干方式不一定相同,如果将来要洗鞋子就是另外一种方法;按照这种方法来写代码,将来拓展或维护起来会比较麻烦,C语言就是面向过程语言
更注重每一步要干什么

  • 2.现代洗衣方式(面向对象)

共有四个对象:人、衣服、洗衣粉、洗衣机
洗衣服的过程:人将衣服放进洗衣机、倒入洗衣粉,启动洗衣机;洗衣机洗衣、甩干;
整个过程主要是:人、衣服、洗衣粉、洗衣机四个对象交互完成的,人不需要关系洗衣机是如何洗衣、甩干
不关注过程,只关注对象
面向对象与面向过程并不是一门语言,而是解决问题的方法,没有好坏之分,都有专门的应用场景

二、类定义和使用

2.1 简单认识类与对象

面向对象程序设计关注的是对象,而对象是现实生活中的实体,比如洗衣机。
在这里插入图片描述
上图的下侧就是对洗衣机(对象)简单的描述(类),该过程称为对洗衣机对象(实体)进行抽象(对复杂事物的重新认识)

  • 简而言之,对象是一个真正的实体,对于这个对象的描述(抽象)称为类;
  • 类是用来对一个实体(对象)来进行描述的,主要描述该实体(对象)具有哪些属性(外观尺寸等),哪些功能(用来干什么)

比如:洗衣机
属性:型号、尺寸、功率、重量等
功能:洗衣、烘干

2.2 类的定义格式

了解到类之后,Java中怎么定义类呢?

  • 在java中定义类时需要用到class关键字,具体语法如下
//创建类
class ClassName{
    field; //字段、属性、成员变量
    method;//行为或者成员方法
}

2.3 定义一个冰箱类

//创建冰箱类(大驼峰命名)
class WashMachine{
    //字段、属性、成员变量:这些成员变量定义在方法外部,类的内部
    public String color;//颜色
    public double weight;//重量
    public double height;//高
    public String brand;//品牌
    
    //行为或者成员方法(小驼峰命名)
    public void washClothes() {//洗衣服
        System.out.println(brand+"洗衣");
    }
    public void setTime(String brand) {
        System.out.println(brand+"定时");
    }
}

成员和成员方法都可以有多个,可以详细说明冰箱(对象)的[参数类型(成员)和功能(成员方法)](类),仅说明了冰箱(对象)能干什么,参数类型,并没说它什么参数,C语言中的结构体类似于类中的部分内容
注:类名必须使用大驼峰定义、成员前写法统一为public、此处写的方法不带static关键字。后面会详解

一般一个文件中只定义一个类
main方法所在的类一般要使用public修饰
public修饰的类必须要和文件名相同
不要轻易修改public修饰的类的名称

三、类的实例化

3.1 什么是实例化

Java中定义了一个类,就相当于有了一个新的类型,与int,float类似,只不过int这些为内置类型,而类是用户自己定义的一个类型,比如上述的WashMachine类。有了这个自定义类型之后,就可以使用这些类来定义实例(或者称为对象)
用类类型创建对象的过程,称为类的实例化,Java中采用new关键字配合类名来实例化对象

        WashMachine ws=new WashMachine();//类的实例化
        WashMachine ws2=new WashMachine();//一个类可以实例化多个对象
  • 使用new关键字后,就会在堆上生成一个WashMachine对象,类的引用就会指向这个对象;并且这个对象有类的属性(color、weight…),只不过没有赋值
    在这里插入图片描述
  • 赋值操作:引用.成员=对应成员的数据;
        ws.color="红色";
        ws.brand="美弟";
        ws.weight=120.83;
  • 赋值之后,可以使用打印函数输出数据
        System.out.println("颜色:"+ws.color);

在这里插入图片描述

  • 对于类中成员方法的使用: 引用.成员方法
        ws.washClothes();

在这里插入图片描述

注意事项

  • new关键字用于创建一个对象的实例
  • 只要new就会产生新的对象实例,即堆区给对象分配新的内存
  • 同一个类可以产生多个实例
  • 访问类的成员使用点.
  • 一个引用只能指向一个对象
        dog1=new Dog();
        dog1=new Dog();
        dog1=new Dog();
        dog1=new Dog(); //dog1这个引用最终指向该new Dog()产生的地址对应的对象
  • 引用不能指向引用
        //引用只能指向对象
        Dog dog1=new Dog();
        //dog2这个引用指向了dog1这个引用所指向的对象
        //即两个引用指向同一个对象
        Dog dog2=dog1;     
  • Dog dog2 = null; 表示dog2这个引用不指向任何对象

例:设计一个方法,交换两个整形变量的值

    class Change{
    public int n;
   }
    //交换两个变量值
    public static void swap(Change a,Change b){
        int tmp=a.n;
        a.n=b.n;
        b.n=tmp;
    }
    public static void main(String[] args){
        Change m1=new Change();
        m1.n=123;
        Change m2=new Change();
        m2.n=321;
        System.out.println("交换前m1="+m1.n+" m2="+m2.n);
        swap(m1,m2);
        System.out.println("交换后m1="+m1.n+" m2="+m2.n);
    }

3.2 类和对象的说明

  • 类只是一个模型一样的东西,用来对实体进行描述(类为图纸,实体为根据图纸建造的建筑)
  • 类是一种自定义类型,可以用来定义变量
  • 一个类可以实例化多个对象(一个图纸可以造很多建筑),实例化出的对象占用实际的物理空间,存储类的成员变量
  • 做个比方:类实例化出的对象就像是根据图纸造出的建筑,类就像是图纸,只设计出建造什么东西,但是单纯类并没有实体存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间

四、this引用

4.1 为什么要有this引用

  • 先看一个日期的例子
class Date{
    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) {
        //构造3个日期
        Date date1=new Date();
        Date date2=new Date();
        Date date3=new Date();
        //设置3个日期
        date1.setDate(2000,1,1);
        date2.setDate(2020,12,31);
        date3.setDate(2025,6,6);
        //打印日期
        date1.printDate();
        date2.printDate();
        date3.printDate();
    }

在这里插入图片描述
以上代码定义了一个日期类,然后main方法中创建了三个对象,并通过Date类中的成员方法对对象进行设置和打印,没有任何问题
当以上代码中Date类中的成员方法的形参名和成员变量名相同时就会出现问题

    public void setDate(int year,int month,int day) {
        year=year;
        month=month;
        day=day;
    }

输出结果为默认值
在这里插入图片描述
原因是在Data类中的成员方法中,year、month、day使用的全部都是局部变量而不是成员变量,因为局部变量优先使用,而局部变量没有值,所以打印的结果为默认值
解决方法:在成员方法中使用this引用

    public void setDate(int year,int month,int day) {
    	//this代表当前对象
        //this.year代表这个year是当前对象的year,year代表形参year
        //当前对象就是new的那个对象,谁调用这个方法this就代表谁
        this.year=year;
        this.month=month;
        this.day=day;
    }

在这里插入图片描述
以上代码相当于以下代码,其中Data this 被编译器给隐藏了

    public void setDate(Date this,int year,int month,int day) {
        this.year=year;
        this.month=month;
        this.day=day;
    }
    //date1.setDate(date1,2000,1,1);
    //date1引用指向的对象地址传给this,也就是说this和data1指向同一对象,
    //那么this的成员变量就是data1的成员变量

当多个对象都在调用多个成员方法,但这多个方法并没有任何对象的说明时,也有可能出错,这就是引入this的原因;建议习惯使用this

4.2 什么是this引用

this引用指向当前对象(成员方法运行时调用该成员方法的对象)——this代表当前对象的引用,在成员方法中所有成员变量的操作,都是通过该引用去访问。只不过所有的操作对用户是透明的,用户不需要来传递,编译器会自动完成

class Date{
    public int year;
    public int month;
    public int day;
    public void setDate(int year,int month,int day) {
        this.year=year;
        this.month=month;
        this.day=day;
        System.out.println("引用this指向对象的地址为:"+this);
    }
}
    public static void main(String[] args) {
        Date d =new Date();
        d.setDate(2015,5,5);
        System.out.println("引用d指向的对象的地址为:"+d);
    }

在这里插入图片描述
this引用指向当前对象可以由以上代码验证

4.3 this的引用特性

  • this的类型:对应类类型引用,即哪个对象调用,this就是哪个对象的引用类型
  • this的使用范围:this只能在成员方法中使用
  • 在成员方法中,this只能引用当前对象,不能再引用其他对象
  • this是成员方法中第一个隐藏参数,编译器自动传递
    以下两种写法虽然不同,但本质一样,第一行只是Data this被隐藏
	public void setDate(int year,...) {...}
    public void setDate(Date this,int year,...) {...}
  • this.成员变量:访问成员变量
  • this.成员方法:访问成员方法
class Date{
    public int year;
    public int month;
    public int day;
    public void setDate(int year,int month,int day) {
        this.year=year;
        this.month=month;
        this.day=day;
        this.printDate(); //访问成员方法printData()
    }
    public void printDate(){
        System.out.println("日期为:"+this.year+"年"+this.month+"月"+this.day+"日");
    }
}
    public static void main(String[] args) {
        Date d =new Date();
        d.setDate(2015,5,5);
    }

在这里插入图片描述

  • this():调用当前类的构造方法

五、对象的构造及初始化

5.1 如何初始化对象

  • 在java方法内部定义一个局部变量时必须要初始化,否则会编译失败;注意:在类中的成员变量可以不用初始化(为默认值),因为类中的成员变量(方法外部,类的内部)不是局部变量(方法内部,类的外部)

编译失败

    public static void main(String[] args) {
        int a;
        System.out.println(a);
    }

编译成功

    public static void main(String[] args) {
        Date a=new Date();
        a.printDate();
        a.setDate(1,2,3);
    }

在这里插入图片描述

  • 通过以上可以发现类中的成员变量可以不用初始化,打印出来的值为默认值,即系统进行”默认初始化“
  • 还发现一个问题,每次对象创建好后调用setDate才能设置日期,比较麻烦,那么对象该如何初始化?

5.2 构造方法

5.2.1 概念

  • 构造方法是一个特殊的成员方法,该方法没有返回值,名字必须与类名相同,在创建对象时,编译器自动调用,并且在整个对象的生命周期内只调用一次
class Date2{
    public int year;
    public int month;
    public int day;
    public Date2(){
        this.year=2001;
        this.month=2001;
        this.day=2001;
        System.out.println("执行了不带参数的构造方法");
    }
}
public class Text {
    public static void main(String[] args) {
        Date2 d=new Date2();
    }
}

在这里插入图片描述
主函数中仅有一条语句,运行后可以发现构造方法被执行了,由此得出构造方法会在实例化对象的时候进行调用!

  • 构造方法可以写多个,实例化一个对象时,编译器会调用合适的构造方法
    public Date2(int year,int month,int day){
        this.year=year;
        this.month=month;
        this.day=day;
        System.out.println("执行了带3个参数的构造方法");
    }
    public Date2(int year,int month){
        this.year=year;
        this.month=month;
        System.out.println("执行了带2个参数的构造方法");
    }
        public static void main(String[] args) {
        //构造方法的调用是发生在实例化对象的时候
        Date2 d1=new Date2(2025,2,19);
        Date2 d2=new Date2(2025,2);
        System.out.println("日期为:"+d1.year+"年"+d1.month+"月"+d1.day+"日");
        System.out.println("日期为:"+d2.year+"年"+d2.month+"月"+d2.day+"日");
    }

在这里插入图片描述
以上两个构造方法构成了方法的重载
构造方法的重要作用就是方便初始化对象的成员

  • 对象的产生,简单来说一定会有两步:为对象分配内存、调用合适的构造方法
  • idea编译器提供了一种创建构造方法的快捷方式,当你想写多个构造方法时,可以鼠标
    右击出现Generate,点击Constructor,按住ctr选择构造方法的个数,点击ok即可
    在这里插入图片描述在这里插入图片描述

5.2.2 构造方法的特性

  • 名字和类名相同
  • 没有返回值,设置成void也不行
  • 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次
  • 构造方法可以重载
  • 构造方法至少有一个,当你没有写任何构造方法时,编译器会自带一个不带参数的构造方法(如下),只不过默认的整个构造方法没有具体的实现,对象的初始化一定会调用构造方法
    public Date2(){
        
    }
  • 当有了带参数的构造方法后,你实例化对象时如果不带参数,那你构造方法就必须得写一个不带参数的构造方法(这种情况下系统不会生成不带参数的构造方法)
class Date2{
    public int year;
    public int month;
    public int day;
    public Date2(int year,int month){
        this.year=year;
        this.month=month;
        System.out.println("执行了带2个参数的构造方法");
    }
        public static void main(String[] args) {
 		//有带参数的构造方法,并且在没有不带参数的构造方法情况下实例化一个不带参数的对象,编译器报错
        Date2 d=new Date2();
    }
  • 构造方法中,可以通过this调用其他构造方法来简化代码
class Date2{
    public int year;
    public int month;
    public int day;
    public Date2(){
    	//此时通过this调用了带有两个参数的构造方法
        this(2025,2);
        System.out.println("执行了不带参数的构造方法");
    }
    public Date2(int year,int month){
        this.year=year;
        this.month=month;
        System.out.println("执行了带2个参数的构造方法");
    }
}
    public static void main(String[] args) {
        Date2 d=new Date2();
    }

在这里插入图片描述
通过this()调用了其他构造方法
注:this(…)必须在当前构造方法中的第一行,并且不能形成环(即不停循环调用形成死循环)

  • 大多情况下构造方法使用public来修饰,特殊情况下会被private修饰

5.3 默认初始化

  • 默认初始化就是实例化对象(不赋值)后,成员变量的默认值
class Cat{
    public byte s1;
    public short s2;
    public int s3;
    public long s4;
    public float s5;
    public double s6;
    public boolean s7;
    public String s8;
    public char s9;
}
public class Text2 {
    public static void main(String[] args) {
        Cat s=new Cat();
        System.out.println("byte默认值为:"+s.s1);
        System.out.println("short默认值为:"+s.s2);
        System.out.println("int默认值为:"+s.s3);
        System.out.println("long默认值为:"+s.s4);
        System.out.println("float默认值为:"+s.s5);
        System.out.println("double默认值为:"+s.s6);
        System.out.println("boolean默认值为:"+s.s7);
        System.out.println("Starting默认值为:"+s.s8);
        System.out.println("char默认值为:"+s.s9);
    }
}

在这里插入图片描述
在Java中,char类型的默认值是’\u0000’,也就是Unicode编码中的空字符;引用类型默认值都是null

5.4 就地初始化

  • 在声明成员变量时,就直接给出了初始值
class Text33{
    public int a=100;
    public int b=200;
    public int c=300;
    public Text33() {
    }
}
public class Text3 {
    public static void main(String[] args) {
        Text33 t=new Text33();
        System.out.println(t.a+" "+t.b+" "+t.c);

    }
}

在这里插入图片描述

六、封装

6.1 封装的概念

面向对象程序的三大特征:封装、继承、多态 . 类和对象阶段主要研究的就是封装性。何为封装呢?简单来说就是套壳屏蔽细节
例如:计算机中,将CPU、显卡等复杂部件封装在电脑机箱内,用户只需按下电源键即可使用,无需了解内部细节。更为安全
封装:将类的实行细节进行了隐藏,对外只提供了一些开放的接口
封装的优点:安全

  • 如何进行封装?
  • 如果在代码层次上实现封装的话,必须由private来实现
  • private:如果成员方法或者成员属性被private所修饰后,那么当前方法只能在当前类中使用

封装前

class People{
    public String name;
    public int age;
    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat(){
        System.out.println(this.name+"正在吃饭");
    }

}
public class Text_ {
    public static void main(String[] args) {
        People p=new People("张三",18);
        p.eat();
    }
}

若我不想让别人看到吃饭是怎么实现的,则可以利用private将成员方法封装起来,这样的话,直接调用成员方法就会失败

封装后

    //People类中
    private void eat(){
        System.out.println(this.name+"正在吃");
    }
        public static void main(String[] args) {
        People p=new People("张三",18);
        p.eat();    //由于eat方法被封装起来,编译器报错
    }

由于eat()方法被private修饰,所以只能在当People类中使用,不可以直接使用,可以通过public修饰的方法来使用eat(),如下

    private void eat(){
        System.out.println(this.name+"正在吃");
    }
    public void fun(){
        eat();
    }
    public static void main(String[] args) {
        People p=new People("张三",18);
        p.fun();
    }

在这里插入图片描述

因为fun方法是公开的,并且fun方法中有eat方法,所以可以通过访问fun方法来间接的访问封装的eat方法
实现封装后,类外就无法直接拿到被封装的内容了
构造方法也可以使用private来修饰,被封装后只能在当前类中进行使用

右击后点击Generate后点击Getter and Setter可以快速设置单个和返回单个字段(成员变量)的值

6.2 访问限定符

  • java中主要通过类的访问权限来实现封装,注意其中default不是使用default来修饰,而是什么都不写

在这里插入图片描述
要理解上表需要先理解包是什么东西
例如:default也就是无任何修饰的类或成员或成员方法
Dog和Main这两个类就是不同包中的类,所以在Main中使用无任何修饰的Dog类会报错
在这里插入图片描述
在这里插入图片描述
而Cat和Dog是同一包中的不同类,所以在Cat类中使用无任何修饰的Dog类和成员方法不会报错
在这里插入图片描述
在这里插入图片描述

6.3 包

6.3.1 包的概念

  • 在面向对象中,提出了一个软件包的概念,为了更好的管理类,把多个类收集在一起成为一组,称为软件包。
    类似于电脑中的文件目录,比如为了更好的管理电脑中的文件,可以对文件分类,如音乐为一个文件夹、视频为一个文件夹… 这里整个音乐文件夹可以看作一个包,而音乐里面又有不同的歌手的歌曲,这里每个不同的歌手就可以看作一个类,则歌手张三,李四就是同一个包中的不同类,而仅对于歌手张三的作曲来说,就是属于同一个包的同一类;子类与非子类需要学习继承后才能理解
  • 在java中也引入了包,包是对类、接口等的封装机制的体现,是一种对类或者接口等很好的组织方式, 比如:一个包中的类不想被其他包中的类所使用。

6.3.2 如何在idea中自定义一个包

采用域名逆置的方法来写,如域名www.baidu.com,则包的写法为com.baidu.www
1、右击src,点击New中的Package

在这里插入图片描述
2、输入包名,例如com.bit.www
在这里插入图片描述
3、创建完成
在这里插入图片描述
4、在创建的包下创建类后,idea会自动生成一行代码,代表当前类所在的包

package com.bit.www;
public class ddd {
}

在这里插入图片描述
ddd类在com包中的bit包中的www包中
如果类创建在src下面,那么就相当于在一个默认的包下面
5、此时可以看到磁盘上面的目录结构已经被idea自动创建出来了
在这里插入图片描述

  • 包还有一个重要作用:在同一个工程当中允许存在相同名称的类,只要处在不同的包中即可,另外包中也可以建包 如下
    在这里插入图片描述

6.3.3 导入包中的一个类

Java中提供了很多类供我们使用,例如Arrays类:可以使用java.util.Arrays导入java.util这个包中的Arrays类

public class Text5 {
    public static void main(String[] args) {
        int []arr=new int[]{1,2,3};
        //使用java.util来使用包中的Arrays类
        System.out.println(java.util.Arrays.toString(arr));
    }
}

上述写法很麻烦,因为如果多次使用Arrays类就要写java.util,所以可以在开头使用import语句导入包

import java.util.Arrays;
public class Text5 {
    public static void main(String[] args) {
        int []arr=new int[]{1,2,3};
        System.out.println(Arrays.toString(arr));
    }
}
  • 如果需要使用java.util中的其他类,可使用java.util.*
import java.util.*;
public class Text5 {
    public static void main(String[] args) {
        int []arr=new int[]{1,2,3};
        System.out.println(Arrays.toString(arr));
    }
}

但是更建议显示的指定要导入的类名,否则容易出现冲突情况

  • 当导入的两个包中都有需要用的类,此时如果按照上述写法就会产生歧义,编译出错,在这种情况下需要使用完整的类名
import java.util.*;
import java.sql.*;
public class dddd {
    public static void main(String[] args) {
        Date d=new Date();
        //报错,因为util和sql包中都有Date类,编译器无法识别Date类属于哪一个包
        System.out.println(d.getTime());
    }
}

6.3.4 常见的包

  • java.lang:系统常用的基础类(String、Object、Math等),使用这些类不需要包导入语句,此包从JDK1.1自动导入
  • java.net:进行网络编程开发包
  • java.sql:进行数据库开发的支持包
  • java.util:是java提供的工具程序包(非常重要)
  • java.io:I/O编程开发包

七、static成员

7.1 学生类引例

class Student {
    public int age;
    public String name;
    public String classRoom;

    public Student(String name, String classRoom) {
        this.name = name;
        //classRoom代表上课的教室
        this.classRoom = classRoom;
    }
}
class Text{
    public static void main(String[] args) {
        Student s1=new Student("张三","101");
        Student s2=new Student("李四","101");
        Student s3=new Student("王五","101");
    }
}

以上3个学生都在一个教室上课,这样信息就重复了3次,如果是100个学生在同一个教室上课,那么信息就重复了100次,内存就会储存100份相同的数据,也就造成了空间的浪费,优化方法:使用static,在Java中,被static修饰的成员称之为静态成员,也可以称为类成员,不属于某个具体的对象,是所有对象所共享的,属于类,存储在方法区

7.2 static修饰成员变量

由于被static修饰的成员变量不属于对象 就不能通过对象的引用来访问,但是不是报错,是警告,使用类名来访问是合理的

        Student.classRoom="2-101";

使用不同对象的引用来访问,操作的都是同一个被static修饰的成员变量

        s1.classRoom="2-102";
        s2.classRoom="2-103";

最终classRoom的值为2-103
总结:
类的成员变量有两种:
1.静态成员变量
1.属于类,不属于对象,仅有1份
2.类名+点号来访问静态成员变量【合理的方法】
3.对象引用+点号来访问静态成员变量【不合理的方法】
2.非静态成员变量(普通成员变量)
1.属于对象,每实例化一个对象,都会有一个对应的非静态成员变量。每个对象都有
2.对应的引用+点号来访问
静态的成员变量随着类被加载而创建,类被卸载而销毁

7.3 static修饰成员方法

static修饰成员方法与static修饰成员变量的特性基本一致,但使用过程中容易出错,见下例

class Student {
    public int age;
    public String name;
    public static String classRoom;
    public static void sleep(){
        //报错
        System.out.println(this.name+"正在睡觉");
    }
    public Student(String name,int age ) {
        this.age = age;
        this.name = name;
    }
}
class Text{
    public static void main(String[] args) {
        Student s1=new Student("张三",23);
        s1.sleep();
    }
}

报错的原因很简单,因为被其修饰的成员方法属于类,而不属于某个具体的对象,不需要实例化就可以使用,而这里使用的非静态的成员变量name就不能使用——在静态方法的内部不能使用非静态的成员,this不能出现在静态方法当中,相反在非静态方法内则可以使用静态成员,静态的使用是较为严格的

7.4 static成员变量初始化

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

  • 就地初始化
    就地初始化指的是在定义时直接给出初始值
    public static String classRoom="教A306";
  • 静态代码块初始化
    什么是静态代码块呢?继续往后看…

八、代码块

8.1 代码块概念及分类

使用{}定义的一段代码称为代码块。 根据代码块定义的位置以及关键字,又可以分为一下四种:

  • 普通代码块
  • 构造快
  • 静态块
  • 同步代码块(多线程部分再谈)

8.2 普通代码块

普通代码块:定义在方法中的代码块

public class Main {
    public static void main(String[] args) {
        //普通代码块,执行不需要条件,永远会被执行
        {
            int a=10;
            System.out.println();
        }
    }
}

8.3 构造代码块

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

 class Dog {
    public int age;
    public String name="大黄";
     {
         //构造代码块||实例代码块
         System.out.println("实例代码块被执行了....");
         age=1;     //一般情况下用来初始化非静态的数据成员
     }
     public Dog( String name) {
         this.name = name;
     }
     public void act(){
        System.out.println(name+"在狗叫");
    }
     public static void main(String[] args) {
         Dog d=new Dog("大黄");
         System.out.print(d.age+"岁的");
         d.act();
     }
 }

在这里插入图片描述

8.4 静态代码块

用static定义的代码块称为静态代码块。一般用于初始化静态成员变量

     static{
         System.out.println("静态代码块被执行了...");
         age=2;
     }
  • 静态代码块和静态成员变量的先后顺序会影响结果,如下
public class uname1 {
    static {
        s1=999;
    }
    public static int s1=10;
    public static void main(String[] args) {
        System.out.println(uname1.s1);
    }
}

在这里插入图片描述

  • 静态代码块是先执行的,其次是实例化代码块被执行,最后是构造方法被执行
  • 静态代码块不依赖于对象,只要加载类就会被执行
  • 静态代码块不管生成多少个对象,其只会执行一次,可以有多个静态代码块
  • 静态成员变量是类的属性,因此是再JVM加载类时开辟空间并初始化的
  • 实例代码块只有在创建对象时才会执行
  • 如果一个类中有多个静态代码块,在编译代码时,编译器会按照定义的先后顺序依次执行(合并)

九、内部类

9.1 内部类概念

类的五大成员:成员属性、成员方法、构造方法、代码块、内部类
内部类: 在类的里面再定义一个类,那么里面的那个类就是内部类,外面的类就是外部类;与这两个类无关的其他的所有类都统称为外部其他类

class A{
//B为内部类
    class B{  
    }
}
  • 内部类的访问特点
  1. 内部类可以直接访问外部类的成员,包括私有
class A{
    private int a=10;
    private void act(){
    }
    class B{
        public void fun()
        {
            System.out.println(a);
            act();
        }
    }
}
  1. 外部类访问内部类必须得创建对象
class A{
    public void act(){
        B b=new B();
        //实例化对象才能访问内部类
        b.fun();
    }
    class B{
        public void fun()
        {
            System.out.println("内部类方法");
        }
    }
}

内部类和外部类共用一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件

9.2 为什么要学习内部类?

比如需要写一个Car类来描述汽车;
属性:汽车名、车龄、颜色,发动机品牌、发动机使用年限
从以上属性可以看出,发动机是一个独立的个体,与车本身还是有区别的,但发动机又依赖车,所以令发动机类为内部类,Car类为外部类最为合理;
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么这个内部的完整结构最好使用内部类。

9.3 内部类的分类

9.3.1 静态内部类

被static修饰的内部类

class A{
    static class B{
    }
}

如何获取静态内部类对象?
直接实例化内部类对象会报错;
可以把静态内部类当做外部类的成员

class A{
    static class B {
    }
}
public class Main1 {
    public static void main(String[] args) {
        A.B b=new A.B();
    }
}

在静态内部类当中,不可以直接访问外部类对象的非静态成员,因为静态类不依赖于对象,不能直接访问

9.3.2 实例内部类

静态内部类去除static修饰符就是实例内部类
实例内部类中出现静态成员变量时必须要由final修饰,因为内部类相当于是外部类的一个成员,当实例化实例内部类的时候,必然需要依赖外部类,而静态成员变量不需要依赖对象,会与之矛盾,所以要使用final修饰

        //常量是在编译的时候确定的
        static final int data6=4;

实例化内部类对象
刚刚说到,可以把内部类当做外部类的成员,而外部类的成员的调用方法如下

class OuterClass{
    public void fun(){
    }
    class InnerClass{
    }
}
public class Main3 {
    public static void main(String[] args) {
        OuterClass out = new OuterClass();
        out.fun();
    }
}

所以实例内部类的对象实例化如下

OuterClass out = new OuterClass();
OuterClass.InnerClass Inn=out.new InnerClass();

等同于以下写法

OuterClass.InnerClass Inn2=new OuterClass().new InnerClass();

9.3.3 匿名内部类

匿名对象

new 类名();//这个对象叫匿名对象
new 类名().成员方法();//使用一次就需要实例化一次

如果以后的场景只使用一次对象,就使用匿名对象
匿名内部类
以下部分可以看作 是一个类 实现了一个接口,并且重写了接口当中的方法

//接口
interface In{
    void func();
}

In a=new In() {
	@Override
    public void func() {
    System.out.println("重写接口方法");
    }
};
//调用func方法
a.fun();

还可以这样调用

new In() {
	@Override
    public void func() {
    System.out.println("重写接口方法");
    }
}.func();

9.3.4 局部内部类

在类中的方法里面定义的类叫局部内部类

class D{
    public void fund(){
        //局部内部类
        class E{
        }
    }
}

局部内部类只能在所在的方法里面实例化对象

class D{
    public void fund(){
        //局部内部类
        class E{
        }
        //实例化时只能在方法里面实例化    
        E e=new E();
    }
}

该内部类使用比较少,几乎不用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值