final、static与抽象

final 关键字

final 修饰变量

  1. final概述

    在 Java中,final 可以修饰变量,修饰方法,修饰类

    • final 修饰的变量:可以初始化,不能在更改
    • final修饰的方法:不能在子类中重写
    • final 修饰的类:不能再被继承派生出子类了
  2. final修饰局部变量

    • 在方法中声明的变量称为局部变量

    • 局部变量加final修饰以后,只能初始化一次,不能再次更改

      • 可以声明的同时初始化
      • 或者在构造函数中初始化
    • 当final修饰引用类型变量时

      • 变量的值就是引用对象的地址值,不能再次修改,但是可以改变该地址中的数据
    • ```java
      package day03;

      import java.util.Arrays;

      /**

      • final修饰局部变量
        */
        public class Demo01 {
        public static void main(String[] args) {
        //在方法中声明的变量就是局部变量
        final int a; //声明一个final类型的变量
        a = 5; //只能够赋值一次 初始化
        System.out.println(a);
        // a = 10; //再次赋值时 会报错 (已初始化 final修饰的局部变量,不能再次修改值)
        final int b = 10; //声明变量的同时初始化
        //b = 10 ; //编译错误 不能再次赋值

        //final 修饰引用类型变量
        //变量中存的是引用类型的首地址 初始化之后不可修改
        final int[] arr = {5,10};
        System.out.println(Arrays.toString(arr));
        arr[0] = 15; //final 修饰的引用类型的局部变量中的数据可以改变
        System.out.println(Arrays.toString(arr));
        //arr = new int[10];//编译错误 final修饰的arr的引用不能改变

        final Aoo aoo = new Aoo();
        //aoo = new Aoo()
        aoo.c = 10;
        System.out.println(aoo.c);
        // aoo = null; aoo引用中保存的是地址 不能修改
        }

      }
      class Aoo{
      int c = 5;

      }




      3. final修饰方法的参数

      - Java中方法的参数也是一种局部变量,只是其声明位置是方法参数,在接收到传递参数时候初始化

      - 在方法参数上可以使用final修饰

      - 也是初始化以后不能再次修改
      - 方法参数在调用方法传递参数值时初始化
      - 在方法运行期间方法参数变量的值不能修改

      - ```java

      public class Demo02 {
      public static void main(String[] args) {
      Boo boo = new Boo();
      //调用方法的同时 相当于为方法的参数赋值
      //在方法执行的过程中,不能修改final修饰的参数
      boo.sum(5,10);
      }
      }
      class Boo{

      public void sum(int a ,final int b){
      a =10;
      //b = 16; //编译错误 不能再次修改变量的值
      System.out.println(a+b);
      }
      }
  3. final 修饰实例变量(类的成员属性)

    • 在类中声明的对象属性,由于是属于每个对象实例的变量所有也称为”实例变量”

    • final可以修饰实例变量

      • 必须直接初始化或者在构造器中初始化
      • 实例变量在初始化以后不能再次改变了
    • ```java
      public class Demo03 {
      public static void main(String[] args) {
      Coo coo1 = new Coo();
      System.out.println(coo1.a+” “+coo1.b);

      Coo coo2 = new Coo(10);
      System.out.println(coo2.a+” “+coo2.b);
      //coo2.b = 15; 不能修饰final修饰的成员属性值
      }
      }
      class Coo{
      final int a =5;
      final int b;
      public Coo(){
      this.b = 3;
      }
      public Coo(int b){//可以使用构造器对final修饰的成员属性赋值
      this.b = b;
      }
      }


      #### final 修饰方法

      使用final修饰的方法不可以被重写

      - final 修饰的方法不会影响方法在当前类中的使用,但是不能在子类中被重写修改
      - 防止在子类中被"不经意"重写

      实际工程中项目中较少使用final方法,原因是很多框架工具都会采用“动态代理”技术代理(重写)对象的功能,实现灵活的软件功能

      ```java
      package day03;

      public class Demo04 {
      public static void main(String[] args) {
      Doo doo = new Doo();
      doo.hello();
      SubDoo subDoo = new SubDoo();
      subDoo.hello();
      }
      }
      class Doo{
      public final void hello(){
      System.out.println("Hello World");
      }
      }
      class SubDoo extends Doo{
      //final修饰的方法可以被继承
      //final 修饰的方法不可以被子类重写
      // public void hello(){

      }

      }

final 修饰类

final 修饰的不可以被继承,无法派生子类

  • ​ 可以避免被继承和重写,避免被子类修改功能
  • java的很多核心API,都是final类型,这样就保护了这些非常重要的API功能

实际开发中也并不建议使用final声明类

package day03;

/**
* final 修饰类
*/
public class Demo05 {
public static void main(String[] args) {
Eoo eoo = new Eoo();
eoo.hello();

}
}
final class Eoo{
public void hello(){
System.out.println("Hello World");
}
}
/*
class SubEoo extends Eoo{ //编译错误 不能继承final修饰的类

}*/

static 关键字

static 静态

  1. 静态变量

    1. 成员变量

      作为类的成员,在类体中声明的变量称为成员变量,成员变量有三种:

      • 实例变量:是属于每个对象的属性,每个对象中都有一份
      • 静态变量:是属于类的变量,只有一份,全体对象共享同一份变量
      • 常量:是不变的常数
    2. 静态变量

      • 使用static修饰类的成员变量,称为静态变量
        • 静态变量只有一份,是可以被全体对象共享的一份变量
      • 用静态变量存储程序中只有一个就够的数据
      • 静态变量和类的信息一起存储在方法区
      • 静态变量是属于类的变量
    3. 静态变量的访问

      static修饰的成员属性不推荐使用对象去访问,推荐用类去访问

      package day03;

      /**
      * static修饰成员属性 被修饰的属性称为静态成员
      * 属于整个类 类的所有对象共享这一个数据
      */
      public class Demo06 {
      public static void main(String[] args) {
      //创建类的对象
      Foo foo1 = new Foo();
      Foo foo2 = new Foo();
      foo1.a = 5;
      foo2.a = 10;
      foo1.b = 10;
      foo2.b = 5;
      System.out.println(foo1.b); //结果是5 因为foo2改掉了
      System.out.println(Foo.b); //推荐用类.直接去访问
      }
      }
      class Foo{
      int a ;//普通的成员属性
      static int b; //static 修饰的属性称为类属性或者类变量 属于整个类 只有一份
      public void hello(){
      //static int age; //static 不能直接修饰局部变量
      }

      }
    4. 静态变量工作原理

    java源文件经过编译得到字节码文件,每个类编译为一个class文件

    当执行java程序时候,每用到一个类Java就会自动将对应的字节码加载到方法区

    • 类中的静态变量此时在方法区中分配出来
    • 字节码文件只加载一次

    自动加载类

    • 创建对象时
    • 访问类的静态属性时
    • 执行类中的静态方法时
    1. static final

    java中同时使用static final关键字声明”常量”,常量用于声明不会变化的量

    • 数学中的圆周率PI,自然数e,等等

    在软件开发中也会将固定不变的数值声明为常量

    package day03;

    /**
    * 常量 static final
    * 常量在声明的时候就要赋值 以后不可以修改
    */
    public class Demo07 {
    public static void main(String[] args) {
    System.out.println(Message.MSG_LOGIN_FAILED);
    }
    }
    class Message{

    //用static final 修饰的常量 名字采用全大写 如果是单词的组合 用 下划线隔开
    public static final String MSG_LOGIN_FAILED = "用户名或密码错误";
    public static final String MSG_LOGIN_SUCCESS = "登录成功" ;
    }

    1. 常量的细节

    软件中不能改变的数据,应该都定义为常量

    同时使用static final修饰,两个关键字的顺序可以调换

    常量必须初始化

    一般常量名都是大写字母,多个单词使用下划线隔开:MAX_VALUE

  2. 静态方法

静态方法:方法声明时候添加了static

属于类的方法,可以直接使用类名引用方法

普通方法可以访问静态成员,静态方法不能访问非静态成员

package day03;

import java.util.Arrays;

/**
* 静态方法 使用Static修饰的方法 可以直接用类名访问
*/
public class Demo08 {
public static void main(String[] args) {
Person.add(2,4); //直接用类访问静态方法
Person person = new Person("Tom");
person.whoru(); //成员方法只能通过对象访问
person.add(2,4);//对象也可以访问静态方法 不推荐
run();
// say(); //不是静态方法 不能被直接访问
//但是可以通过创建对象进行访问
Demo08 demo08 = new Demo08();
demo08.say();
}
public static void run (){
System.out.println("Hello");
}
public void say(){
System.out.println("HELLO");
}
}
class Person{
String name;
static int num = 23;
public Person(String name){
this.name = name;
}
public static void add(int a,int b){
System.out.println(a + b);
//静态方法属于整个类 不能使用this
//静态方法不能访问非静态成员
System.out.println(num);
// //但是可以通过创建对象进行访问
Person person = new Person("Tom");
person.whoru();
}
public void whoru(){
System.out.println("我是:"+this.name);
//普通方法可以访问静态成员
System.out.println(num);
add(3,6); //普通方法可以访问静态成员
}

}
  1. 静态方法的细节

    package day03;

    /**
    * 代码块 在类中直接使用{}编写的一段代码
    */
    public class Demo09 {
    public static void main(String[] args) {
    Goo goo1 = new Goo();
    Goo goo2 = new Goo();

    System.out.println(goo1.name);
    System.out.println(goo2.name);
    }
    }
    class Goo{
    String name;
    //每一次创建对象 都会执行代码块中的内容
    {
    name = "Tom";
    System.out.println("属性成功初始化");
    }
    }

  2. static 其他用法

    • 代码块

      在类中直接使用{}编写的一段代码

    • 静态代码块

      • 类中使用static修饰的代码称为静态代码块

      • 静态代码块在类加载期间执行

        • java类只加载一次,所以静态代码块也只执行一次
        • 因为这个特性,经常用静态代码块初始化类中的静态属性
      • ```java
        package day03;

        /**

        • 静态代码块

        • 加载类的时候(创建类的对象 或者访问类的静态成员)执行 以后不再执行
          */
          public class Demo10 {
          public static void main(String[] args) {
          //java会在创建对象之前自动加载类
          // Hoo hoo = new Hoo();
          // Hoo hoo1 =new Hoo();
          String name = Hoo.name;
          System.out.println(name);
          }
          }
          class Hoo{
          static String name;

          /**

          • 静态代码块只能访问静态成员和静态方法一样
            */
            static {
            System.out.println(“静态成员初始化成功”);
            name =”Tom”;
            }

        }


        3. 静态导入

        ```java
        package day03;

        import static1.Person;
        import static static1.Person.*; //静态导入一个类中的所有静态成员

        /**
        * 静态导入
        */
        public class Demo11 {
        public static void main(String[] args) {
        Person.eat();
        Person.sleep();
        Person.eat();
        //导入以后不需要加类名 就可以直接访问类中的静态成员
        eat();
        sleep();
        talk();
        }
        }

        package static1;

        public class Person {
        public static void talk(){
        System.out.println("在说话");
        }
        public static void eat(){
        System.out.println("在吃饭");
        }
        public static void sleep(){
        System.out.println("在睡觉");
        }
        }

abstract抽象

抽象类
  1. 什么是抽象类
    • 使用关键字abstract声明的类是抽象类,抽象类不能直接实例化创建对象
    • 在面向对象设计时候,会利用“泛化”将子类的共同属性和方法抽取出来设计出父类,此时的父类往往是半成品类,只包含部分属性和方法,甚至属性值都没有合理初始化,如果直接创建对象并且使用有可能造成各种不理想结果,甚至是异常故障
  2. 抽象类不可以实例化
    • 抽象类不可以实例化
      • 面向对象设计时候根据子类泛化得到的半成品父类,应该定义为抽象类,这样可以限制创建半成品的对象,减少意外的错误发生
      • abstract 和 final 不可以同时修饰一个类:final 关键字使得类不可被继承,而抽象类如果不能被继承则没有任何意义
    • 如何使用抽象类
      • 在类名前面添加abstract关键字
      • 抽象类可以作为父类被子类继承,可以定义变量
      • 抽象类不能直接创建对象

抽象方法

  1. 什么是抽象方法

    • 抽象方法:使用abstract关键字声明,不包含方法体
    • 在利用泛化设计父类的时候,可能会出现全体子类都有相同的方法
      • 但是每个具体方法的实现都不相同
      • 只能将方法名抽取到父类
      • 方法体留在每个子类中
      • 把只有方法名的方法,定义为抽象方法
  2. 抽象方法的语法

  • 使用abstract关键字声明方法,且不能有方法体
  • 抽象类可以没有抽象方法, 但是包含抽象方法的类必须声明为抽象类
    • 包含抽象方法的半成品,必须保证不能被实例化
  • 子类继承抽象类的时候,必须重写(实现)其抽象方法,否则出现编译错误
    • 可以将抽象方法看作父类对子类的行为约定,必须被子类重写实现
  1. 抽象类的意义
  • 为其子类提供一个公共的类型

  • 封装子类中的重复内容(成员变量和方法)

  • 定义有抽象方法,子类虽然有不同的实现,但该方法的定义是一致的