第二节面向对象

第七章认识面向对象

1.什么是对象?

在Java中,对象(Object)是指一个具体事物的实例,任何事物都可以使用对象(类)来描述,如猫、狗、计算机、杯子、云、水、空气、叶子、灰尘等看得见的、看不见的、宏观的、微观的、具体的、抽象的都是对象,总之"万物皆对象";

对象是实际存在的个体。(真实存在的个体)

宋小宝就是一个对象
姚明就是一个对象
刘德华就是一个对象

宋小宝、姚明、刘德华这3个对象都属于“明星”。

2.面向对象的程序设计

Java是一门面向对象的编程语言,面向对象是一种程序设计思想,与之对应的还有面向过程程序设计;面向对象是把一个对象的特征(属性)和行为单独封装到对象源代码中;这些属性和行为都被集中到一个地方,这样比把方法或者过程与数据分散开来更为方便和安全,含义更加明确;

3.面向对象和面向过程

举例子:开车去上班

  • 面向过程:去车库提车、拿钥匙、打火、踩离合、挂挡、踩油门、刹车、加油、到公司、找车库停车
  • 面向对象:打个滴滴(对象)、到公司

可以看得出来,面向过程关注的是步骤,将所有步骤连在一起"我就能到公司上班",面向对象则关注的是"开车去上班"这个事物整体,完成这件事的步骤全部封装起来,交给指定的对象去做(司机);

tips:也是个人的理解

面向过程主要关注的是:实现步骤以及整个过程。过程A-B-C 这个三个过程都有因果关系.
如果这三个过程中有一个过程发生错误,
就如上述例子一样,如果去车库提车,如果车库钥匙丢了呢?那么整个过程无法进行

面向对象主要关注的是:对象A,对象B,对象C,然后对象ABC组合,或者CBA组合…
这单个对象或者是对象的组合完成一个特定的功能
如果这其中一个对象发生了错误,那么就可以修改发生错误的就可以了
就如上述的例子,如果滴滴司机中途跟你说:实在不好意思,不能接单,那么就可以重新打

4.类和对象

在现实世界中,属于同一类的对象很多,类是抽象的,不是具体的,我们人习惯以对象的方式认识现实世界;

例如:学生这个类。如果单说这个学生的话,会觉得抽象,这个学生的学号呢?姓名呢?性别呢?等等

那么 这个学生就是一个模板(类),通过这个模板我们可以创建很多实体化的东西(实例化对象)。
比如:我创建了一个学生的模板(类) 学号:001,姓名:张三,性别:男

通过上述的例子就可以认识到 类和对象的关系了, 我们通过模板来创建一个实例。也就是通过类创建对象

tips:类是抽象的,对象是具体的,对象是类的实例化;

类是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征行为特征来描述该类事物。

  • 属性:该事物的状态信息;
  • 行为:该事物的功能信息;

第八章创建对象和使用

1.Java类的定义

public class 类名 {
	//成员变量
    //成员方法
}
  • 定义类: 就是定义类的成员,包括成员变量和成员方法。
  • 成员变量: 和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外。
  • 成员方法: 和以前定义方法几乎是一样的。只不过把static去掉,static的作用在面向对象后面再讲解。

类的定义格式举例:

/*
注意:不需要写main方法,Student只是一个模板而已
*/
public class Student {
    //成员变量(属性) 描述事物的信息
    String name;        //姓名
    int age;            //年龄

    // 成员方法(行为) 描述事物的行为
    public void study(){
        System.out.println("学习");
    }

    public void eat(){
        System.out.println("吃饭");
    }
}

2.类的实例化(创建对象)

类是抽象的,不是具体的,类只是负责把事物描述起来,提供模板;对象是类的实例化,是具体的;我们定义好一个Java类后需要通过对象将类进行实例化;

创建对象的语法:new

类名 对象名=new 类名()

使用对象访问类中的成员:引用.的方式(这里又称对象名为引用,意思就是只想堆内存的指针)

对象名.成员变量;
对象名.成员方法();

类的实例化练习:

public class Demo {
    public static void main(String[] args) {
        //创建对象格式: 类名 对象名 = new 类名();
        Student s = new Student();

        System.out.println("s: " + s); //s: com.song.Student@1540e19d 这里是为什么后面解释

        //直接输出成员变量的值
        System.out.println("姓名: " + s.name);      //null
        System.out.println("年龄" + s.age);         //0
        System.out.println("--------");

        //给成员变量赋值
        s.name = "张三";
        s.age = 21;

        //再次输出成员变量的值
        System.out.println("姓名: " + s.name);      //张三
        System.out.println("年龄: " + s.age);       //21
        System.out.println("---------");
        
        //调用成员方法
        s.study();          //学习
        s.eat();            //吃饭
    }
}

3.成员变量的默认值

在上方的案例当中,我们直接输出了成员变量的值 name(String类型) 为null ,age(int类型)为0

那么 就有了成员变量的默认值

基本类型:

数据类型默认值
整数(byte,short,int,long)0
浮点数(float,double)0.0
字符(char)‘\u0000’
布尔(boolean)false

引用数据类型

数据类型默认值
数组,对象,Stringnull

4.JVM内存

在上面"方法"一章说到了JVM有三大内存空间,只说到了方法区和栈区。接下载就说说堆内存

  • 方法区:类加载器classloader,将硬盘上的XXX.class字节文件码装载到jvm的时候,会将字节码文件放到方法区中。也就是方法区中存储的是代码片段,因为类需要加载,所以方法区中最先有数据
  • :在方法被调用的时候,该方法需要的内存空间在栈中分配。
    栈式使用最频繁的,一直压栈和弹栈
    方法调用的时候压栈,栈中最主要的存储时局部变量,局部变量时在方法当中的。
    就比如,我们先执行main方法,压入栈中,我们称main方法栈帧
  • 堆内存:凡是用过,new运算符创建的对象,都在堆内存当中。new运算符的作用就是在堆内存中开辟一块空间。堆中存储“对象”,以及对象的实例变量

上图!

5.创建对象练习以及JVM内存图

Student类:

public class Student {
    //属性(描述状态),在java程序中以“成员变量”的形式存在
    //学号
    int no;//这种变量又称为实例变量,一个对象一份
    //姓名
    String name;
    //年龄
    int age;
    //性别
    boolean sex;
    //住址
    String addr;
}

类的实例化:

public class StudentTest02 {
    public static void main(String[] args) {
        /*
            访问学生姓名可以直接通过类名吗
            学生姓名是一个实例变量,实例变量是对象级别的变量
            是不是应该先有对象才能说姓名的事情
            不能通过”类名“来直接访问”实例变量“
            System.out.println(Student.name);
         */
        
        //应该注意,现有类才能创建对象,而有了对象,我们才能去对变量进行操作
        //创建对象一
        //s1这个局部变量叫做引用
        Student02 s1=new Student02();
        //创建对象二
        //s2这个局部变量也叫引用

        //怎么访问实例变量
        //语法: 引用(通过new出来的变量).实例变量名
        System.out.println(s1.no);
        System.out.println(s1.name);
        System.out.println(s1.sex);
        System.out.println(s1.age);
        System.out.println(s1.addr);
        System.out.println("-------------------------");
        Student02 s2=new Student02();
        //这里的s1 s2都是属于局部变量
        System.out.println(s2.no);
        System.out.println(s2.name);
        System.out.println(s2.sex);
        System.out.println(s2.age);
        System.out.println(s2.addr);
        System.out.println("-------------------------");



        //代码到此处我可以修改学生s1的学生属性吗
        //通过”=“赋值的方式将内存中实例变量的值修改一下
        s1.no=110;
        s1.name="张三";
        s1.age=20;
        s1.sex=true;
        s1.addr="唐山学院";
        System.out.println(s1.no);
        System.out.println(s1.name);
        System.out.println(s1.sex);
        System.out.println(s1.age);
        System.out.println(s1.addr);
    }
}

JVM内存图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TgqyG5x8-1670474925478)(https://foruda.gitee.com/images/1664349585560247395/4755859a_10637153.png)]

6.成员变量和局部变量

根据声明的位置不同,变量也有不同的名称,当然存放的位置也可能不同,下面进行代码测试

public class Student {
    int age;//成员变量 年龄

    String name;//成员变量 姓名

    //成员方法 学习方法
    public  void study(){
        int i = 10; //局部变量
        System.out.println("学生正在学习");
    }

    /**
     * 自我介绍的方法
     * @param name 姓名(局部变量)
     * @param age  年龄(局部变量)
     */
    public void intro(String name,int age){
        System.out.println("大家好我叫"+name+"我今年"+age+"岁了");
    }
}

成员变量和局部变量的区别:

  • 成员变量:
    • 成员变量定义在类中,在整个类中都可以被访问。
    • 成员变量分为类成员变量和实例成员变量,实例变量存在于对象所在的堆内存中
    • 成员变量有默认初始化值。
    • 成员变量的权限修饰符可以根据需要,选择任意一个
  • 局部变量:
    • 局部变量只定义在局部范围内,如:方法内,代码块内等。
    • 局部变量存在于栈内存中,当方法弹栈(执行完毕)后,局部变量销毁;
    • 局部变量没有默认初始化值,使用前必须手动赋值;
    • 局部变量声明时不指定权限修饰符;

就现在而言总体来说:在方法内部的就是局部变量存放的位置在栈区, 在方法外部的就是成员变量 存放的位置为堆区或者是方法区(这个到后面将static会解释)

7.方法调用传参问题

类:

class ren{
    int age;
}

测试类:

/*
java中关于方法调用时参数传递实际上只有一个规则
不管你是什么类型的数据,实际上传递的时候,都是将变量中保存的那个值复制一份传过去
*/
public class Test {
    public static void main(String[] args){
        ren r=new ren();
        r.age=10;
        add(p);
        /*
        这里是将一个对象的内存地址传到了add中,
        也就是add中的对象和main中的对象 保存的地址都指向了堆内存同一个类型的对象
        但是我们要注意 main中的p 和add中的p 是两个p
        */
        System.out.println("main--->"+p.age);//11

    }
    //方法中的参数可以是基本的也可以是引用的
    public static void add(ren r){
        //这里会让堆内存中age这个属性下存放的值发生改变
        p.age++;
        //理解为c语言的指针,形参和实现指向同一内存地址
        System.out.println("add--->"+p.age);//11
    }
}
/*
最后输出为
add--->11
main--->11
*/

在此需要明确什么是值传递和址传递

  • 值传递:(参数类型是基本数据类型):方法调用时,实参把它的值传递给对应的形参,形参只是用实参的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形参值的改变不影响实参的值。
  • 引用传递:(参数类型是引用数据类型参数):也称为传地址。方法调用时,实参是对象(或数组),这时实参与形参指向同一个地址,在方法执行中,对形参的操作实际上就是对实参的操作,这个结果在方法结束后被保留了下来,所以方法执行中形参的改变将会影响实参。

在Java中,除了基本数据类型之外的数据类型都是引用数据类型,都是通过new在堆内存开辟空间;

对上方的代码,内存图解释

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6tvjxWiC-1670474925481)(https://foruda.gitee.com/images/1664360660659307046/855491e0_10637153.png)]

8.this关键字

  1. this是一个关键字,是一个引用,保存内存地址指向自身

  2. this可以使用在实例方法中,也可以使用在构造方法中

  3. this出现在构造方法中其实代表的是当前对象

  4. this不能使用在静态方法中

  5. this大部分情况下是可以省略的,在区分局部变量和实例变量的时候不能省略

  6. this()这种语法只能出现在构造方法中的第一行,表示当前构造方法调用本类其他的构造方法

    public class ThisTest(){
        int year;
        int month;
        int day;
        
        public date(){
            this(1970,1,1);
        }
        
        public date(int year,int month,int day){
            this.year=year;
            this.month=month;
            this.day=day;
        }
        public static void main(String[] a){
            date d1=new date();//1970,1,1
            date d2=new date(1999,2,1);//1999,2,1
        }
    }
    

this内存结构图

通过图解可以看出,this的中保存的内存地址和引用是一样的

9.static关键字

9.1static概述

static 关键字它可以用来修饰的成员变量成员方法被修饰的成员是属于类的,而不是单单是属于某个对象的。被static修饰的成员由该类的所有实例(对象)共享;

static修饰的统一都是静态的,都是类相关的,不需要new对象,之间采用”类名.“访问当一个属性是类级别的属性,所有对象的这个属性的值都是一样的,建议定义为静态变量

9.2定义和使用格式

9.2.1类变量

static 修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改该类变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作,因为该变量属于类,而不是某个对象。

类变量: 使用 static关键字修饰的成员变量。

定义格式:

static 数据类型 变量名;

使用格式:

类名.变量名

例如:

static String name;

如果一个类当中,每个对象都有公共的字段呢?每次new都会实例化一个新的公共字段,那显然是浪费内存的,所以有没有一种方法可以把公共的属性提出来.就是static关键字(到下面会讲解静态原理图)

public class StaticTest {
    public static void main(String[] args) {
        //访问中国人的国际
        //静态变量应该使用"类名."的方式访问
        System.out.println(Chinese.guoji);
        Chinese c1=new Chinese("123","张三");
        System.out.println(c1.name);
        System.out.println(c1.zheng);
        Chinese c2=new Chinese("456","李四");
        System.out.println(c2.name);
        System.out.println(c2.zheng);
    }
}
class Chinese{
    //身份证号
    //每个人的身份证号不同,所以身份证号都应该是实例变量,一个对象一个
    String zheng;
    //名字也是一个人一个
    String name;
    //对于zhongguo这个类来说,国际都是中国,不会随对象的改变而改变。
    //国籍属于整个类的特征
    //加static的变量叫静态变量
    //静态变量在类加载时初始化,不需要new对象,静态变量的空间就开出来了
    //静态变量存储在方法区
    static String guoji="中国";
    //无参数
    public Chinese(){

    }
    //有参数
    public Chinese(String zheng,String name){
        this.zheng=zheng;
        this.name=name;
    }
}
9.2.2 静态方法

static 修饰成员方法时,该方法称为类方法。静态方法在声明中有 static ,建议使用类名来调用,而不需要创建类的对象。调用方式非常简单。

类方法:使用 static关键字修饰的成员方法,习惯称为静态方法

定义格式:

修饰符 static 返回值类型 方法名 (参数列表) { 
	// 执行语句
}

举例:在Student类中定义静态方法

public static void showId(int id) {
    System.out.println("id:" + id);
}

静态方法调用的注意事项:

  • 静态方法可以直接访问类变量(被static修饰的变量)和静态方法。(静态方法能够访问静态资源)
  • 静态方法不能直接访问普通成员变量或成员方法。反之,成员方法可以直接访问类变量或静态方法。
  • 静态方法中,不能使用this关键字

tips:静态方法只能访问静态成员。

9.2.3调用格式
// 访问类变量
类名.类变量名;

// 调用静态方法
类名.静态方法名(参数)

总结:

成员变量和成员方法的访问:引用.的形式

  • 引用.成员变量
  • 引用.成员方法

这里要注意,要先有对象(先new对象)才能去使用引用.的形式否则是无法访问

静态变量和静态方法

  • 静态变量:类名.类变量名
  • 静态方法:类名.静态方法名(参数)

9.3静态原理图解

static 修饰的内容:

  • 是随着类的加载而加载的,且只加载一次。
  • 存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。
  • 它优先于对象存在,所以,可以被所有对象共享。

在JDK1.8以前,静态成员存储在方法区永久代)中,此时方法区的实现叫做永久代

在JDK1.8以后,永久代被移除,此时方法区的实现更改为元空间,但由于元空间主要用于存储字节码文件,因此静态成员的存储位置从方法区更改到了堆内存中

9.4静态代码块

  1. 使用static关键字可以定义:静态代码块

  2. 定义格式

    static{
            java语句
        }
    
  3. static静态代码块在什么时候执行的呢?

    • 类加载时执行,并且只执行一次,静态代码块由这样的特征
  4. 静态代码块在类加载时执行,并且在main方法之前执行

  5. 静态代码块是自上而下顺序执行

  6. 静态代码块的作用

    • 第一:静态代码块不这么常用(不是每个类都要写的东西)
    • 第二:静态代码块这种语法机制实际上是sun公司给我们java程序员的一个特殊的实际这个时机叫类加载时机

9.5实例代码块

执行的时机:只有时构造方法,必然在构造方法之前,执行一次

语法:

   {
       java语句;
   }

9.6 Static的应用-单例设计模式

  • 单例是一种设计模式,是为了解决某个问题
  • 单例能解决的问题:保证一个类对外只能产生一个对象。

重点在于如何实现单例:

  1. 饿汉单例

    • 特点:拿对象时,对象已经存在

    • 实现步骤:

      /**
       * 饿汉单例设计模式
       */
      public class SingleInstance {
          // 1.定义一个类,将构造器私有
          private SingleInstance() {}
      
          // 2.对外提供一个静态对象
          // 饿汉单例是在获取对象前,就已经准备好了一个对象
          // 这个对象只能通过类名访问,所以定义成static的
          public static SingleInstance instance = new SingleInstance();
      }
      
  2. 懒汉单例

    • 特点:要拿对象的时候,才开始创建一个对象。

    • 实现步骤:

      /**
       * 懒汉单例设计模式
       */
      public class SingleInstance2 {
          // 2.定义一个静态的成员变量负责存储一个对象,只加载一次,只有一份。
          // 专业的做法是:这里私有化,这样可以避免给别人挖坑
          private static SingleInstance2 instance2;
      
          // 1.将构造器私有化
          private SingleInstance2() {}
      
          // 3.对外提供一个静态的获取对象的方法
          public static SingleInstance2 getInstance2() {
              if (instance2 == null) {
                  // 第一次来拿对象 :此时需要创建对象。
                  instance2 = new SingleInstance2();
              }
              return instance2;
          }
      }
      

10.构造方法

10.1概念

什么是构造方法呢?

  • 构造方法是一个特殊的方法,通过构造方法可以完成对象的创建,以及实例化变量的初始化。换句话说:构造方法是用来创建对象,并且同时给对象的属性赋值
    注意:实例变量没有手动赋值的时候,系统会赋默认值
  • 也就是当我们new 的时候 就会调用构造方法

在类当中没有写构造方法为什么还能调用呢?

  • 当一个类没有提供任何构造方法时,系统会默认提供一个无参数的构造方法(而这个构造方法叫做缺省构造器)

构造方法的语法?

  • [修饰符列表] 构造方法名 (形式参数列表){
    				构造方法体;
    }
    
  • 注意:

    • 第一:修饰符列表统一写public 不要写成public static
    • 第二:构造方法名必须和类名一样
    • 第三:构造方法不需要指定返回值类型,也不能写void,写上以后就是普通方法了

构造方法的特点

  • 构造方法是支持方法重载
  • 在一个类中构造方法可以有多个,并且所有的构造方法名字都是一样的
  • 对于实例变量来说,只要你在构造方法中没有手动给他赋值,统一都会默认赋值,默认赋值系统值

实例变量是什么时候完成初始化的呢?

​ 实例变量是在构造方法执行的过程中完成初始化的

9.2代码测试

vip类:

/*
1.构造方法可以有多个.
2.如果我们没有写构造方法,那么系统会默认提供一个无参数的构造方法(缺省构造器)
3.构造方法支持方法的重载,可以写一个参数的构造方法,可以写连两个参数的构造方法.也可以写全参的构造方法
4.如果我们写了构造方法,那么无参的构造方法就不在提供,所以为了保险起见 我们要把无参数的构造方法手动写上
*/
public class Vip {
    long no;
    String name;
    String birth;
    boolean sex;
    
    public Vip(){

    }
    public Vip(long no,String name){
        //给实例变量赋初值【初始化实例变量,初始化属性】
        this.no=no;
        this.name=name;
        //这里实际上还有两行代码
        //this.birth=null
        //this.sex=false
    }
    public Vip(long no,String name,String birth){
        no=no;
        this.name=name;
        this.birth=birth;
        //这里实际上还有一行代码
        //this.sex=false
    }
    public Vip(long no,String name,String birth,boolean sex){
        this.no=no;
        this.name=name;
        this.birth=birth;
        this.sex=sex;
    }
}

代码测试:

public class ConstructorVip {
    public static void main(String[] args) {

        //无参构造方法
        Vip u1=new Vip();
        System.out.println(u1.no);
        System.out.println(u1.name);
        System.out.println(u1.birth);
        System.out.println(u1.sex);
        
        //有参构造方法
        Vip u4=new Vip(3333L,"黑寡妇","2001-2-03",false);
        System.out.println(u4.no);
        System.out.println(u4.name);
        System.out.println(u4.birth);
        System.out.println(u4.sex);
      
    }

}

11.package和import

  1. 为什么要用package

    • package是java中的包机制。包机制的作用是为了方便程序的管理
    • 不同功能的类分别存放在不同的包下。(按照功能划分的,不同的软件包具有不同的功能)
  2. package怎么用?

    • package是一个关键字,后面加包名
    • 注意:package语句只运行出现在java源代码的第一行
  3. 包名命名规范

    • 一般采用公司域名倒叙的方式(因为公司域名具有全球唯一性)
    • 公司域名倒叙名+项目名+模块名+功能名
  4. 编译带有包的java程序

    • 编译

      javac -d .  Ptest.java
      
    • 运行

      java  packagetest.Ptest01// packagetest.Ptest这个是类名
      

12.访问权限

访问权限有那些呢?

  • private私有的
  • protected受保护的
  • public公开的
  • 默认的(什么也不写)
访问控制修饰符本类访问同包访问子类访问编写位置任意
public可以可以可以可以
protected可以可以可以不行
默认可以可以不行不行
private可以不行不行不行

访问控制权限可以修饰什么?

  • 属性(四个都可以)
  • 方法(四个都可以)
  • 类(public和默认可以)
  • 接口(public和默认可以)

第九章面向对象的三大特征

1.封装

1.1封装概述

封装是面向对象的三大特征之一,面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改。封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性

举例:

public class Person { 
    int age;
}

这里我们先不封装来分析一下程序的缺点:

  • 如果不去封装,那么这个属性就是对外暴露的,随便去访问都可以.这显然是不安全的。
    举个例子 如果我们给Person的age属性赋值为300呢?
    那显然是不合理的。人的年龄怎么会超过300岁呢?
    所以我们要对age属性进行封装。加强安全性!

1.2private关键字

  1. private是一个权限修饰符,代表最小权限。
  2. 可以修饰成员变量和成员方法。
  3. 被private修饰后的成员变量和成员方法,只在本类中才能访问。

封装的原则:将属性隐藏起来,若需要访问某个属性,提供公共方法对其访问(setter和getter方法)

使用private 修饰成员变量,代码如下:

public class Person { 
    //年龄
    //private表示私有的,被这个关键字修饰之后,该数据只能在本类中访问
    //出了这个类,age属性就无法访问了
    private int age;
    
	//封装的第二步:对外提供的公开的set(写) get(读)方法作为操作入口(并且都不带static,都是实例方法)
    /*
    注意:
        在java开发中 get方法和set方法应该满足一下格式
        public 返回值类型 get+属性名首字母大写(无参){}
        public void set+首字母大写(有一个参数){xxx=参数;}
    */
    
     public int getAge(){
        return age;
    }
    public void setAge(int age){
        //能不能在这里设置关卡
        if (nianling>150 ||nianling<0) {
            System.out.println("对不起,您输入的年龄有问题");
            return;
        }
        //程序到了这里说明,年龄合法的了
        this.age = age;

    }
    
    
}

总结:

1.封装
第一点要记住:有个封装才能有继承,有了继承才能有多态
2.面向对象得首要特征:封装
作用:保证内部得安全性,屏蔽复杂,暴露简单
3.在代码级别上,封装有什么用?
一个类体当中得数据,假设封装之后,对于代码得调用人员来说,不需要关心代码得复杂实现,只需要通过一个简单得入口就可以访问了。
另外,类体中安全级别较高得数据封装起来,外部人员不能随意访问,来保证数据得安全性
4.封装的代码实现两步
第一步:属性的私有化
第二步:一个属性对外提供set和get方法。外部程序只能通过set方法修改,只能通过get方法读取,可以在set方法中设立关卡来保证数据的安全性
在强调一下:set和get方法都是实例方法,不能带有static
不带static的方法称为实例方法,实例方法的调用必须现有new对象
set方法:
public void set+属性名首字母大写(一个参数){xxx=一个参数}
get方法:
public 返回值类型 get+属性名首字母大写(无参){return xxx; }

1.3 工具类的定义

工具类是什么?

  • 工具类中都是静态方法,为了完成一个共用功能

好处:一次编写,处处可调,提高代码的复用性,提高开发的效率。

注意:建议把工具类的构造器私有化,因为工具类无需对外创建对象,它的方法都是静态方法,直接用类名访问即可。

public class CodeUtil {
    // 验证码工具类
    // 静态成员方法
    // 由于工具类中的方法都是静态方法,直接用类名调用即可,
    // 所以,建议将工具类的构造器使用private修饰
    private CodeUtil() {

    }

    public static String createCode(int n) {
        String code = "";
        String data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random r = new Random();
        for (int i = 0; i < n; i++) {
            int index = r.nextInt(data.length());
            code += data.charAt(index);
        }
        return code;
    }
}

2.继承

2.1继承概述

  1. 子类继承父类可以获得父类的功能,提高代码的复用性
  2. 子类可以重写(覆盖)某些父类的功能,我们一般称为增强
  3. 子类除了可以继承父类的功能之外,还可以额外添加子类独有的功能。
  4. 继承的作用:
    • 基本作用:子类继承父类,代码可以得到复用
    • 主要作用:因为有了继承关系,才有后期的方法的覆盖和多态机制

那么在实际开发中,满足什么条件才使用继承呢?

凡是采用“is a”能描述的,都可以去继承

例如:cat is a Animal 猫是一个动物,dog is Animal 狗是一个动物

假设以后的开发中有一个A类,有一个B类,A类和B类确实有重复的代码,那么他们两个之间可以继承吗?
不一定,还要看他们之间是否可以用is a去描述

2.2继承语法

class 父类
    
class 子类 extends 父类

注意点:

  • B类继承A类,则称A类为超类(superclass)、父类、基类,B类称为子类(subclass)、派生类、扩展类
  • java中的继承只支持单继承,不支持多继承
  • 虽然java中不支持多继承,但有的时候会产生间接继承的效果

2.3案例

举例:

  1. 老师
    • 属性(成员变量):姓名年龄性别
    • 行为(成员方法):吃饭睡觉授课
  2. 学生
    • 属性(成员变量):姓名年龄性别
    • 行为(成员方法):吃饭睡觉听课

1)分析一下这两个对象有什么公共的部分,姓名、年龄、性别、吃饭、睡觉 都是公共的部分。只有授课和听课不同

定义父类,提取公共的部分

//提取公共部分,封装到Person类中
public class Person{
    String name;//姓名
    int age;//年龄
    boolean sex;//性别
    
    //吃饭的行为
    public void eat(){
        System.out.println(name+"正在吃饭");
    }
    
    public void sleep(){
        System.out.println(name+"正在睡觉");
    }
}

2)定义一个老师的类,继承Person类,也就是将父类的属性和行为都继承过来.自己扩展一个授课的行为

public class Teacher extends Person{
	public void teach(){
        System.out.println(name+"授课");
    }
}

3)定义一个老师的类,继承Person类,也就是将父类的属性和行为都继承过来.自己扩展一个上课的行为

public class Student extends Person{
	public void study(){
        System.out.println(name+"学习");
    }
}

4)测试类

public class Test{
    public static void main(String[] args){
        //创建教师对象
        Teacher t = new Teahcer();
        t.name="张三";
        t.age=38;
        t.sex=true;//true表示男人
        t.teach();
        t.eat();
        t.sleep();
        
        //创建学生对象
        Student t = new Student();
        t.name="李四";
        t.age=21;
        t.sex=false;//true表示男人
        t.study();
        t.eat();
        t.sleep();
    }
}

2.4父类不可继承的内容

父类中所有的内容,不是都可以让子类继承的,以下两个内容就不可以让子类继承

  • 被private修饰的(这里访问要通过setter和getter方法)
  • 构造方法不能被继承

2.5成员变量的继承

当继承父类之后,成员变量的方法名重名和不重名是否有冲突呢?

2.5.1成员变量不重名

子类和父类不重名的情况下,直接继承过来没有任何的影响

public class Demo {
    public static void main(String[] args) {
        Zi zi=new Zi();
        System.out.println("父类num:"+zi.num1);//父类继承下来的
        System.out.println("子类num:"+zi.num2);//这个是本身的
    }
}

class Fu{
    int num1=10;
}

class Zi extends Fu{
    int num2=20;
}

结果输出

父类num:10
子类num:20
2.5.2成员变量重名

当成员变量重名的时候呢?

public class Test {
    public static void main(String[] args) {
        Zi zi=new Zi();
        System.out.println(zi.num);
        System.out.println(zi.num);
    }
}

class Fu{
    int num=10;
}

class Zi extends Fu{
    int num=20;
}

测试结果

20
20

经过测试可以得出,当子类和父类的成员变量名重名的时候,就会采用就近原则使用子类的成员变量

2.6成员方法的继承(重写)

2.6.1什么是方法的重写

如果子类和父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法;

但是如果是重名的时候呢?
和成员变量的继承是一样的,当成员方法重名的时候会调用子类的方法,这么机制叫做方法的重写(Override)

在此回顾以下方法的重载:

当一个类中,如果功能相似的话,建议将名字定义的一样,这样代码美观
条件一:在同一个类中
条件二:方法名相同
条件三:参数列表不同(个数、顺序、类型)

2.6.2什么时候采用重写机制

子类继承父类之后,放继承过来的方法无法当前子类的业务需求时,子类有权力对这个方法进行重新编写.则有必要进行方法的覆盖(重写),

在前面就提到,成员方法描述的是,对象的行为。当子类和父类行为有一些差异的时候,但都属于一种同一种行为,只是行为方式不一样.就比如父类采用的学习方式是线下课(行为),而子类采用学习的方式是网课。显然这两种行为都是学习,但只是方式不同,所以我们要进行方法的重写。

举例 :没有使用方法的重写

public class OverrideTest {
    public static void main(String[] args) {
        //创建鸟儿对象
        Bird b1=new Bird();
        //让鸟儿移动
        b1.move();//动物在移动
    }
}
//父类
class Animal{
    public void move(){
        System.out.println("动物在移动");
    }
}
//子类
class Bird extends Animal{
    //子类继承父类中,有一些“行为”可能不需要改进,有一些“行为”可能面临这必须改进
    //因为父类中继承过来的方法已经无法满足子类的需求

    //鸟儿在移动的时候希望输出鸟儿在飞翔

}

class Cat extends Animal{
    //猫在移动的时候,我希望输出:猫在走猫步
}

上方案例的改进

public class OverrideTest {
    public static void main(String[] args) {
        bird b=new bird();//鸟儿在飞行
        b.move();
        Dog c=new Dog();//狗在走狗步
        c.move();

    }
}
class animal{
    public void move(){
        System.out.println("动物在移动");
    }
}
class bird extends animal{
    //对move方法进行方法的覆盖,方法的重写
    //最好将父类中的方法原封不动的复制过来
    //方法覆盖,就是将继承过来的那个方法覆盖掉了,继承过来的方法没了
    public void move(){
        System.out.println("鸟儿在飞行");
    }
}

class Dog extends animal{
    public void move(){
        System.out.println("狗在走狗步");
    }
}

结论:

​ 当子类对父类继承过来的方法进行”方法覆盖“之后,
​ 子类对象调用该方法的时候,一定执行覆盖之后的方法

2.6.2重写的注意点
  • 条件一:两个类必须要有继承关系
  • 条件二:重写之后的方法和之前的方法具有:
    相同的返回值类型
    相同的方法名
    相同的形式参数列表
  • 条件三:访问权限不能更低,可以更高(子类重写的方法,访问权限不能更低只能更改)关于访问权限后面会有讲解
  • 条件四:重写之后的方法不能比之前的方法抛出更大的异常,可以更少(异常会到后期有讲解)

这里还有几个注意事项:

注意一:方法覆盖只是针对方法,和属性无关

注意二:私有方法无法覆盖

注意三:构造方法不能被继承,所有构造方法也不能被覆盖

注意四:方法覆盖只是针对于实例方法,静态方法覆盖没有意义

2.6.3方法重载和方法覆盖有什么区别?
  • 方法重载发生在同一个类当中。
  • 方法覆盖是发生在具有继承关系的父子类之间。
  • 方法重载是一个类中,方法名相同,参数列表不同。
  • 方法覆盖是具有继承关系的父子类,并且重写之后的方法必须和之前的方法一致:
    方法名一致、参数列表一致、返回值类型一致。

2.7super关键字

写super关键字之前,思考两个问题

  • 当子类的成员变量名称和父类的成员变量名称重名的时候,那么我们怎么访问父类的成员变量呢?
  • 当子类的成员方法名称和父类的成员方法名称重名的时候,那么我们怎么访问父类的成员方法呢?

这个时候就体现了super的重要性了

super关键字:用于修饰父类成员变量,类似于之前学过的 thisthis代表的是本类对象,而super代表的是父类对象;使用super我们可以调用父类的成员(属性和行为),注意super关键字不能访问父类私有(private修饰)的成员

子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量(不是private修饰的)时,需要使用 super 关键字,修饰父类成员变量,类似于之前学过的 this

super关键字的作用:

  • 用于访问父类中定义的属性
  • 用于调用父类中定义的成员方法
  • 用于在子类构造方法中调用父类的构造器
2.7.1.this和super的图解

先上示例

public class SuperTest {
    public static void main(String[] args) {
        Zi zi = new Zi();
        System.out.println(zi.num);
        zi.method();
    }
}
class Fu{
	private int num=20;
    public void method(){
        System.out.println("父类的method");
    }
    
}
class Zi extends Fu{
	int num=10;
    public void method(){
        System.out.println("父类的method");
    }   
}

在这里插入图片描述

  • main方法进栈执行;
  • 执行new Zi(),首先现将Fu、Zi两个类加载到内存(方法区)
  • 初始化Fu类,再初始化子类;子类中保留父类的引用super;可以通过super来获取父类的非私有内容;
  • 子类调用method方法,调用的是this区中的方法,因此输出的是Zi method…
2.7.2super()

super,是手动调用还是自动的去调用呢?

示例:

public class SuperTest {
    public static void main(String[] args) {
        Zi zi1=new Zi();
        Zi zi2=new Zi(20);

    }
}
class Fu{
    String name;
    int age;

    public Fu() {
        System.out.println("Fu类的无参数构造方法执行了");
    }

    public Fu(String name, int age) {
        System.out.println("Fu类的全参构造方法执行了");
        this.name = name;
        this.age = age;
    }
}

class Zi extends Fu{
    int id;
    
    public Zi(){
        System.out.println("Zi类的无参数构造方法执行了");
    }

    public Zi(int id){
        System.out.println("Zi类的有参数构造方法执行了");
        this.id=id;
    }
}

测试内容

Fu类的无参数构造方法执行了
Zi类的无参数构造方法执行了
Fu类的无参数构造方法执行了
Zi类的有参数构造方法执行了

得出结论:

  • 在初始化子类的时候(new),会先调用父类的无参数构造方法,来实例化父类(也就是先有父后有子)

  • 所以在父类当中,一定要手动写上无参数的构造方法.这样才能不会出错,确保父类一定会实例化

  • 我们没有手动写上super这个关键字,也会调用父类的无参数构造方法。那会是什么位置呢?

  • 在测试结果上我们可以看出,"Fu类的无参数构造方法执行了"总是比"Zi类的无参数构造方法执行了"先执行,那么系统一定会在构造方法的首行写调用super这个关键字的,由于是调用的构造方法
    所以在首行会默认的有一个super()(this()是调用本类的构造方法,super()是调用父类的构造方法)

    class Zi extends Fu{
        int id;
        
        public Zi(){
            super();
            System.out.println("Zi类的无参数构造方法执行了");
        }
    }
    

注意:父类的无参数构造方法一定要手动写出来

注意:在前面说到this()也是出现在构造方法的首行的,所以this()和super()不能共存

2.7.3.访问父类

在前面说到用private修饰的无法去访问,那么既然学了super 就可以调用父类的setter和getter方法去访问

如果成员变量和成员方法和父类的一样那么怎么去访问呢?

示例:

 class Fu{
    private String name;

    public Fu() {
    }

    public Fu(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void method(){
        System.out.println("父类的方法");
    }
}

class Zi extends Fu{
    public void method() {
        System.out.println("子类的方法");
        //System.out.println(super.name);父类私有成员不可以通过super获取
        System.out.println(super.getName());// 获取父类的方法
        super.method();//执行父类的方法
    }
}
2.7.4.this和super对比的去学

this关键字:

  • this能出现在实例方法中和构造方法中
  • this的语法是:”this.“ ”this()“
  • this不能使用在静态方法中。
  • this. 大部分是可以省略的
  • this. 在区分句部变量和实例变量时候不能省略
  • this()只能出现在构造方法第一行中,通过当前的构造方法去调用”本类“中
    其他的构造方法,目的是:代码复用

super关键字

  • super能出现在实例方法中和构造方法中
  • super的语法是:”super.“ ”super()“
  • super不能使用在静态方法中。
  • super. 大部分是可以省略的
  • super. 在什么时候不能省略呢??
    父类和子类中有同名属性,或者说有同样的方法,
    想在子类中访问父类的 super.不能省略
  • super()只能出现在构造方法第一行中,通过当前的构造方法去调用”父类“中
    的构造方法,目的是:创建子类对象的时候,先初始化父类特征。

3.多态

3.1多态概述

面向对象的三大特性——封装、继承、多态;前两个我们都学习过了,多态也是面向对象中一项重大的特征,多态体现了程序的可扩展性、代码的复用性等;

多态:即事物(对象)存在的多种形态,简称多态;

在生活中,多态处处看见;如"我要吃水果",这个水果有很多种形态,苹果、梨子、香蕉等都是水果;再比如说话,猫的叫声是喵喵,狗的叫声是汪汪,苍蝇的叫声蝇蝇等;同一行为,通过不同的事物,可以体现出来的不同的形态;

3.2多态案例

现有一家宠物店:宠物店有寄存宠物的功能,但是运营之初,只能寄存狗,针对狗这个类,有吃饭、睡觉的行为;

多态的用法,就是基于这个案例来实现的

3.2.1不使用多态的写法

1)定义狗类:

public class Dog {
    public void eat(){
        System.out.println("狗狗正在吃饭");
    }
    public void sleep(){
        System.out.println("狗狗正在睡觉");
    }
}
  1. 定义一个宠物店
public class PetShop {
    //存放狗
    public void DogShop(Dog dog){
        dog.eat();
        dog.sleep();
    }
}

但是运营一段时间以后,宠物店强大了起来,就可以寄存猫了

1)定义猫类

public class Cat {
    public void eat(){
        System.out.println("小猫正在吃饭");
    }
    public void sleep(){
        System.out.println("小猫正在睡觉");
    }
}

2)扩展原来PetShop宠物店类

public class PetShop {
    //存放狗
    public void DogShop(Dog dog){
        dog.eat();
        dog.sleep();
    }
    
    //存放猫
    public void DogShop(Cat cat){
        cat.eat();
        cat.sleep();
    }
}

到这里需要注意了,如果宠物店又强大可以寄存鹦鹉呢?那是不是还要在之前的宠物店这个类继续扩展,如果扩展一个,两个觉得没啥,但是要扩展上百个上千个呢。还总是要修改一些重复的代码

3.2.2多态的写法

1)定义所有宠物的父类

public class Animal {
    public void eat(){
        System.out.println("动物正在吃饭");
    }
    public void sleep(){
        System.out.println("动物正在睡觉");
    }
}

2)修改上方的狗类去继承宠物类

public class Dog extends Animal {
    public void eat(){
        System.out.println("狗狗正在吃饭");
    }
    public void sleep(){
        System.out.println("狗狗正在睡觉");
    }
}

3)修改上方的猫类去继承宠物类

public class Cat extends Animal{

    public void eat(){
        System.out.println("猫猫正在吃饭");
    }
    public void sleep(){
        System.out.println("猫猫正在睡觉");
    }
}

4)修改上方的宠物店类

public class PetShop {
    //存放所有的动物
    public void AnimalShop(Animal animal){// Animal animal=new Dog();
        animal.eat();
        animal.sleep();
    }
}

可能在这里还是不知道 这哪里是多态了呢?最后寄存宠物的时候,存放的是所有宠物的父类而不是存放的各个宠物啊。

下面进行一个测试代码:

public class Test {
    public static void main(String[] args) {
        //先创建一个宠物店类
        PetShop petShop=new PetShop();
        //创建狗狗类
        Dog dog=new Dog();
        //寄存动物
        petShop.AnimalShop(dog);
    }
}

测试结果:

狗狗正在吃饭
狗狗正在睡觉

根据测试结果,在宠物店寄存方法中,参数是所有宠物的父类

public void AnimalShop(Animal animal){
        animal.eat();
        animal.sleep();
}

那为什么输出的狗狗在吃饭和睡觉呢?

我们new的是狗狗的对象,是宠物类的子类,在寄存方法中接收到的参数为

Animal animal=new Dog();

这就引出了多态的用法,向上转型和向下转型 下面详细讲解

3.3多态用法

多态的前提

  1. 继承或者实现(实现到后面来讲)
  2. 方法的重写(子类方法的重写)
  3. 父类引用指向子类对象

一句话概括多态就是父类引用指向子类对象,在上一章案例中就是父类引用(Animal)指向了子类对象(Dog、Cat),多态一般伴随着重写的出现,调用者是父类,具体执行的方法则是子类(子类重写了父类的方法运行的是子类),因为只有子类的功能才能凸显多态性,不同的子类具备的功能是不一样的,那么有重写肯定就会有继承或者实现;

多态的格式

父类类型 变量名 = new 子类对象;
变量名.方法名();

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写方法。

3.4多态的转型

转型分为:

  • 向上转型:自动类型提升;多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。当父类引用指向一个子类对象时,便是向上转型。子类型重写的方法
    子----->父(自动类型转化)----> 父类 引用名 = new 子类();
  • 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。子类型特有的方法
    父----->子(强制类型转换,需要加强制类型转换符) 子类 引用名 = (子类)父类引用;

tips:

向上转型和向下转型可以理解为自动类型转换和强制类型转换

自动类型转换:小范围(子类)—>大范围(父类)

强制类型转换:大范围(父类)—>小范围(子类)

3.4.1向上类型转换

子类重写父类的方法,在调用方法的时候 会指向子类的方法

示例:水果摊

  1. 定义水果的父类

    public class Fruit {
    
        public void buy(){
            System.out.println("客户买了水果");
        }
    }
    
    
  2. 定义苹果类

    public class Apple extends Fruit{
        public void buy() {
            System.out.println("客户买了苹果");
        }
    }
    
    
  3. 定义香蕉类

    public class Banana extends Fruit{
        public void buy() {
            System.out.println("客户买了香蕉");
        }
    }
    
    
  4. 定义客户类

    public class Customer {
     
        //这里Fruit fruit=new Apple()
    	//Fruit fruit=new Banana()
        public void buyFruit(Fruit fruit){
            fruit.buy();
        }
    }
    
    
  5. 测试类

    public class Test {
        public static void main(String[] args) {
            //创建用户类
            Customer customer=new Customer();
            //这个就是两行代码的合并 Apple apple =new Apple(); customer.buyFruit(apple);
            //用户购买苹果
            customer.buyFruit(new Apple()); 
            //用户购买香蕉
            customer.buyFruit(new Banana());
        }
    }
    
  6. 测试结果

    客户买了苹果
    客户买了香蕉
    

在客户类当中

fruit.buy();

java程序分为编译阶段和运行阶段

先来分析编辑阶段
对于编译器来说,编译器只知道fruit的类型是Fruit
所以编译器在检测语法的时候,会去 Fruit.class字节码文件找到buy()方法
找到了,绑定上buy()方法,编译通过,静态绑定成功。(编译阶段属于静态绑定)
再来分析运行阶段:
运行阶段的时候,实际上在堆内存中创建的java对象是Apple/Banana对象
所以buy()的时候,真正参与buy()的对象是Apple/Banana,所以
运行阶段会动态指向Apple/Banana对象的buy()方法
这个过程属于运行阶段的绑定(运行阶段绑定属于动态绑定)
多态表示多种形态
编译的时候一种形态(编译的时候是水果)
运行的时候一种形态(运行的时候是一只苹果)

3.4.2向下类型转换

父类要用子类特有的方法

示例:

  1. 定义动物类

    public class Animal {
        public void move(){
            System.out.println("动物在移动");
        }
    }
    
    
  2. 定义猫类

    public class Cat extends Animal {
        //Cat子类 对父类进行重写
        public void move(){
            System.out.println("cat在走猫步");
        }
        //猫除了move之外,应该有自己特有的行为(动作、方法)
        //例如:抓老鼠
        public void zha(){
            System.out.println("抓老鼠");
        }
    }
    
  3. 测试类

    public class Test01{
     	public static void main(String[] args) {
            //new 一个Animal对象
            Animal animal = new Animal();
            //这样就是 向上转型,子类重写父类的方法 也就是父子共有的方法
            animal.move();
            //如果我们访问子类特有的方法呢 zha
            //animal.zha(); 这样编译就有错误
            //大范围(父类)--访问子类特有的方法-->小范围(子类)需要转型
            Cat cat = (Cat)animal;
            //这样就可以
            cat.zha();
        }
    }
    

解释:为什么animal.zha()为什么会编译会报错

对于编译器来说,编译器只知道animal的类型是Animal
所以编译器在检测语法的时候,会去 Animal.class字节码文件找到zha()方法
没有找到zha()方法,编译不通过,静态绑定失败。

3.5instanceof关键字

我们需要知道只要强制类型转换就会有风险,基本类型的强制类型转换会有损失精度的风险。

那么引用类型的强制类型转换也会有风险

举例:

  1. 定义动物类

    public class Animal {
        public void run(){
            System.out.println("动物在跑");
        }
    }
    
    
  2. 定义狗类

    public class Dog extends Animal {
        public void run(){
            System.out.println("狗狗在跑步");
        }
    }
    
  3. 定义猫类

    public class Cat extends Animal{
        public void run(){
            System.out.println("猫猫在跑步");
        }
    }
    
  4. 测试:

    public class Test {
        public static void main(String[] args) {
            Animal animal=new Dog();
            Cat cat=(Cat)animal;
            cat.run();
        }
    }
    
  5. 根据测试结果:控制台输出了java.lang.ClassCastException类型转换异常,

    • 在编译的时候,我们new的是一个Animal 对象 编译通过
    • 在运行的时候,Animal这个对象 底层是一个Dog类型的对象
    • 在进行强制类型转换的时候 Dog和Cat没有继承关系,所以转换失败

那么我们怎么去解决呢?

instanceof关键字:

instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符,它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。

语法:

boolean result = 变量名 instanceof 数据类型;

意思就是 判断,这个变量是否是这个数据类型

对上方测试进行修改,也就是 判断 animal是否是一个猫 如果是一个猫 才去转型

public class Test {
    public static void main(String[] args) {
        Animal animal=new Dog();
        if(animal instanceof Cat){
            Cat cat=(Cat)animal;
        }else{
            System.out.println("不是该类型!");
        }

    }
}

第十章 final关键字

  • final是java语言的一个关键字
  • final表示最终的,不可变的
  • final可以修饰变量以及方法,还有类等
  • final修饰的局部变量,一旦赋值,不能重新赋值,final只能赋值一次
  • final修饰的方法无法被覆盖(重写)
  • final修饰的类无法被继承
  • final修饰的实例变量必须手动赋值,也就是必须赶到系统赋默认值之前赋值
  • static final联合修饰的变量为常量。常量名建议全部大写,每个单词用下划线
    public static final double PI=3.1415926;

修饰类:

  • final修饰的类不能被继承。提高安全性,提高程序的可读性;

    final class A{
    }
    class B extends A{     // 错误,被final修饰的类不能被继承。
    }
    
    

修饰方法:

  • final修饰的方法不能被子类重写;

    class A{
        public final void method(){
            System.out.println("hello");
        }
    }
    class B extends A{
        public void method(){   // 错误,被final修饰的方法不能被重写
            System.out.println("Hi");
        }
    }
    
    

修饰变量:

  • final修饰的变量(成员变量或局部变量)只能被赋值一次;因此被final修饰的变量我们称为常量,通常全大写命名;

    class  A{
        private final double PI = 3.14;     //  声明常量
        public void print(){
            PI = 3.15;          // 错误,被final修饰的变量是常量(不可更改值)
        }
    }
    
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值