Java笔记7类变量类方法接口

63 阅读10分钟

类变量/静态变量

定义

类变量也叫静态变量/静态属性 在类加载的时候就生成了 可以通过类名访问
是同一个类所有对象共享的变量 任何一个该类的对象去修改时 修改的也是同一个变量
定义语法:访问修饰符 static 数据类型 变量名;或者static 访问修饰符 数据类型 变量名;
访问语法:类名.类变量名;对象名.类变量名;(前提是满足访问修饰符的访问权限和范围)

实例

一群小孩在玩游戏 求现在共有多少小孩在玩
共有3个小孩加入了游戏

public class Test {
    public static void main(String[] args) {
        int count=0;
        Child child1=new Child("jack");
        child1.join();//jack加入了游戏
        child1.count++;

        Child child2=new Child("tom");
        child2.join();//tom加入了游戏
        child2.count++;

        Child child3=new Child("smith");
        child3.join();//smith加入了游戏
        child3.count++;

        System.out.println("共有"+Child.count+"个小孩加入了游戏");//共有3个小孩加入了游戏
    }
}
class Child{
    private String name;
    public static int count=0;
    public Child(String name){
        this.name=name;
    }
    public void join(){
        System.out.println(name+"加入了游戏");
    }
}

内存布局

image.png

使用细节

  1. 类变量在类加载时就初始化了 即使没有创建对象 只要类加载了就可以使用类变量了
  2. 类变量的生命周期是随着类加载开始 随类消亡销毁

类方法/静态方法

定义

定义语法:访问修饰符 static 数据返回类型 方法名(){};或者static 访问修饰符 数据返回类型 方法名(){};
调用语法:类名.类方法名(参数列表);或者对象名.类方法名(参数列表);(前提是满足访问修饰符的访问权限和范围)
方法中不涉及任何和对象相关的成员 则可将方法设计成静态方法 提高开发效率 如工具类的方法utils

实例

计算学生的学费

public class Test {
    public static void main(String[] args) {
        Student tom=new Student("tom");
        Student.payFee(100);//tom.payFee(100);

        Student mary=new Student("mary");
        Student.payFee(200);//mary.payFee(200);

        Student.showFee();//总学费是300
    }
}
class Student{
    private String name;
    public static int fee=0;//创建一个静态变量来积累学生的学费
    public Student(String name){
        this.name=name;
    }
    public static void payFee(double fee){
        Student.fee+=fee;
    }
    public static void showFee(){
        System.out.println("总学费是"+Student.fee);
    }
}

不想创建实例 只调用某个方法(当作工具用)

public class Test {
    public static void main(String[] args) {
        System.out.println(MyTools.calSum(10,30));//40.0
        System.out.println("9开方的结果是"+Math.sqrt(9));//9开方的结果是3.0
    }
}
class MyTools{
     public static double calSum(double... nums){
         int res=0;
         for(int i=0;i< nums.length;i++){
             res+=nums[i];
         }
         return res;
     }
}

使用细节

  1. 类方法和普通方法都是随着类的加载而加载 将结构信息存储在方法区
  2. 类方法中无this的参数
    普通方法中隐含着this的参数
  3. 类方法可以通过类名调用 也可以通过对象名调用
    普通方法和对象有关 需要通过对象名调用 比如对象名.方法名(参数) 不能通过类名调 用
  4. 类方法中不允许使用和对象有关的关键字 比如this和super 普通方法(成员方法)可以
  5. 类方法中只能访问静态变量或静态方法
    普通成员方法 既可以访问非静态成员 也可以访问静态成员。

main方法

image.png

代码块/初始化块

定义

属于类中的成员 类似于方法 将逻辑语句封装在方法体中 通过{}包括起来
没有方法名 没有返回 没有参数 只有方法体 而且不通过对象或类显式地调用 而是加载类时 或创建对象时隐式调用
语法:修饰符 {代码};

使用细节

  1. 修饰符可选 要写的话 也只能写static
  2. 代码块分为两类 使用static修饰的叫静态代码块 没有static修饰的 叫普通代码块/非静态代码块
  3. 逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
  4. ;号可以写上 也可以省略
  5. 相当于另外一种形式的构造器(对构造器的补充机制) 调用顺序优先于构造器 可以做初始化的操作
  6. 场景:如果多个构造器中都有重复的语句 可以抽取到初始化块中 提高代码的重用性

实例

public class Test {
    public static void main(String[] args) {
        Movie movie = new Movie("你好,李焕英");
        System.out.println("===============");
        Movie movie2 = new Movie("唐探 3", 100,"陈思诚");
    }
    //电影屏幕打开...
    //广告开始...
    //电影正是开始...
    //Movie(String name) 被调用...
    //===============
    //电影屏幕打开...
    //广告开始...
    //电影正是开始...
    //Movie(String name, double price, String director) 被调用...
}
class Movie {
    private String name;
    private double price;
    private String director;

    //3个构造器 重载
    //下面的三个构造器都有相同的语句 这样代码看起来比较冗余 可以把相同的语句放入到一个代码块中 这样当不管调用哪个构造器创建对象 都会先调用代码块的内容
    {
        System.out.println("电影屏幕打开...");
        System.out.println("广告开始...");
        System.out.println("电影正是开始...");
    }
    public Movie(String name) {
        System.out.println("Movie(String name) 被调用...");
        this.name = name;
    }
    public Movie(String name, double price) {
        this.name = name;
        this.price = price;
    }
    public Movie(String name, double price, String director) {
        System.out.println("Movie(String name, double price, String director) 被调用...");
        this.name = name;
        this.price = price;
        this.director = director;
    }
}

注意事项

  • static代码块也叫静态代码块 作用就是对类进行初始化 而且它随着类的加载而执行 并且只会执行一次
    普通代码块 每创建一个对象 就执行一次
  • 类什么时候被加载
    1. 创建对象实例时(new)
    2. 创建子类对象实例 父类也会被加载
    3. 使用类的静态成员时(静态属性 静态方法)
  • 普通的代码块 在创建对象实例时 会被隐式的调用 被创建一次 就会调用一次
    如果只是使用类的静态成员时 普通代码块并不会执行
  • 创建一个对象时 在一个类调用顺序是:
    1. 调用静态代码块和静态属性的初始化(注意:静态代码块和静态属性初始化调用的优先级一样 如果有多个静态代码块和多个静态变量初始化 则按他们定义的顺序调用)
    2. 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样 如果有多个普通代码块和多个普通属性初始化 则按定义顺序调用)
    3. 调用构造器

单例设计模式

p407

final关键字

  • 使用fina修饰类 属性 方法和局部变量的情况:
    1. 当不希望类被继承时 可以用final修饰类
    2. 当不希望父类的某个方法被子类覆盖/重写(override)时 可以用final修饰方法 访问修饰符 final 返回类型 方法名
    3. 当不希望某个局部变量或类的的某个属性的值被修改 可以用final修饰变量 访问修饰符 final 数据类型 变量名=值
//不希望类被继承时 可以用final修饰类
final class A{
}

//不希望父类的某个方法被子类覆盖/重写(override)时 可以用final修饰方法
class B{
    public final void hi(){}
}

//不希望类的的某个属性的值被修改 可以用final修饰属性
class C{
    public final double TAX_RATE=0.08;//常量
}

//不希望某个局部变量的值被修改 可以用final修饰变量
class D{
    public void cry(){
        final double NUM=0.01;//局部常量
    }
}

使用细节

  • final修饰的属性又叫常量 一般用XX_XX_XX来命名
  • final修饰的属性在定义时 必须赋初值 并且以后不能再修改 赋值可以在以下位置之一
    1. 定义时:如public final double TAX_RATE=0.08;
    2. 在构造器中
    3. 在代码块中
class A{
    //在定义时赋值
    public final double TAX_RATE=0.08;
    
    //在构造器中赋值
    public final double TAX_RATE2;
    public A(){
        TAX_RATE2=1.1;
    }
    
    //在代码块赋值
    public final double TAX_RATE3;
    {
        TAX_RATE3=2.2;
    }
}
  • 如果final修饰的属性是静态的则初始化的位置只能是
    1. 定义时
    2. 在静态代码块
    3. 不能在构造器中赋值
class A{
    //在定义时赋值
    public static final double TAX_RATE=0.08;
    
    //静态在代码块赋值
    public static final double TAX_RATE3;
    static {
        TAX_RATE3=2.2;
    }
}
  • final类不能继承 但是可以实例化对象
  • 如果类不是final类 但是含有final方法 则该方法虽然不能重写 但是可以被继承
  • 一般来说,如果一个类已经是final类了 就没有必要再将方法修饰成final方法
  • final不能修饰构造方法(即构造器)
  • final和static往往搭配使用 效率更高 不会导致类加载 底层编译器做了优化处理
class A {
    public static final double TAX_RATE = 0.08;
    static {
        System.out.println("税率是"+TAX_RATE);
    }
}
  • 包装类(Integer,Double,Float, Boolean等都是final) String也是final类

实例

计算圆形面积

public class Test {
    public static void main(String[] args) {
        Circle circle=new Circle(5);
        System.out.println("面积="+circle.calArea());//面积=78.5
    }
}
class Circle {
    private final double PI = 3.14;
    private double radius;

    public Circle(double radius){
        this.radius=radius;
    }
    public double calArea(){
        return PI*radius*radius;
    }
}

抽象类

父类中的某些方法 需要声明 又不确定如何实现时 可以用abstract关键字修饰 将其声明为抽象方法 那么这个类就要被abstract修饰 就是抽象类

  • 用abstract关键字来修饰一个类时这个类就叫抽象类访问修饰符 abstract 类名{}
  • 用abstract关键字来修饰一个方法时这个方法就是抽象方法访问修饰符 abstract 返回类型 方法名(参数列表); 没有方法体
  • 抽象类的价值更多作用是在于设计 是设计者设计好后 让子类继承并实现抽象类

使用细节

  • 抽象类不能被实例化
  • 抽象类不一定要包含abstract方法 也就是说 抽象类可以没有abstract方法
  • 一旦类包含了abstract方法 则这个类必须声明为abstract
  • abstract 只能修饰类和方法 不能修饰属性和其它的
  • 抽象类可以有任意成员(抽象类本质还是类)比如 非抽象方法 构造器 静态属性等等
  • 如果一个类继承了抽象类 则它必须实现抽象类的所有抽象方法 除非它自己也声明为abstract类
  • 抽象方法不能用private final static修饰 因为这些关键字都是和继承 重写相违背的

实例

编写一个Employee类 声明为抽象类 包含如下三个属性:name, id, salary 提供必要的构造器和抽象方法:work()
对于Manager类来说 他既是员工 还具有奖金(bonus)的属性
请使用继承的思想 设计CommonEmployee类和Manager类 要求类中提供必要的方法进行属性访问 实现work() 提示"经理/普通员工名字工作中”

public class Test {
    public static void main(String[] args) {
        Manager jack=new Manager("jack",999,50000);
        jack.setBonus(8000);
        jack.work();//经理jack工作中

        CommonEmployee tom=new CommonEmployee("tom",888,20000);
        tom.work();//普通员工tom工作中
    }
}
abstract class Employee{
    private String name;
    private int id;
    private double salary;

    public Employee(String name, int id, double salary){
        this.name=name;
        this.id=id;
        this.salary=salary;

    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }

    public abstract void work();
}

class Manager extends Employee{
    private double bonus;

    public Manager(String name, int id, double salary) {
        super(name, id, salary);
    }

    public double getBonus() {
        return bonus;
    }
    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    @Override
    public void work() {
        System.out.println("经理"+getName()+"工作中");
    }
}
class CommonEmployee extends Employee{
    public CommonEmployee(String name, int id, double salary) {
        super(name, id, salary);
    }

    @Override
    public void work() {
        System.out.println("普通员工"+getName()+"工作中");
    }
}

模板设计模式

抽象类体现的就是一种模板模式的设计 抽象类作为多个子类的通用模板 子类在抽象类的基础上进行扩展 改造 但子类总体上会保留抽象类的行为方式

  • 当功能内部一部分实现是确定 一部分实现是不确定的 这时可以把不确定的部分暴露出去 让子类去实现
  • 编写一个抽象父类 父类提供了多个子类的通用方法 并把一个或多个方法留给其子类实现 就是一种模板模式
    实例p429

接口

定义

接口就是给出一些没有实现的方法 封装到一起 到某个类要使用的时候 再根据具体情况把这些方法写出来
语法:

interface 接口名{
    //属性
    //抽象方法
}
class 类名 implements 接口{
    自己属性;
    自己方法;
    必须实现的接口的抽象方法;
}

接口是更加抽象的抽象的类 抽象类里的方法可以有方法体 接口里的所有方法都没有方法体(Jdk8.0后接口类可以有静态方法 默认方法 也就是说接口中可以有方法的具体实现)
体现了程序设计的多态和高内聚低耦合的设计思想

实例

image.png

public class Test {
    public static void main(String[] args) {
        MysqlDB mysqlDB = new MysqlDB();
        t(mysqlDB);//连接mysql 关闭myssql
        OracleDB oracleDB = new OracleDB();
        t(oracleDB);//连接 oracle 关闭 oracle
    }
    public static void t(DBInterface db) {
        db.connect();
        db.close();
    }
}

interface DBInterface{//项目经理
    public void connect();//连接
    public void close();//关闭
}

class MysqlDB implements DBInterface{//A项目
    public void connect(){
        System.out.println("连接mysql");
    }
    public void close(){
        System.out.println("关闭myssql");
    }
}

class OracleDB implements DBInterface{//B项目
    public void connect() {
        System.out.println("连接 oracle");
    }
    public void close() {
        System.out.println("关闭 oracle");
    }
}

使用细节

  1. 接口不能被实例化
  2. 接口中所有的方法是public方法 接口中抽象方法可以不用abstract修饰
  3. 一个普通类实现接口 就必须将该接口的所有方法都实现
  4. 抽象类实现接口 可以不用实现接口的方法
interface IA{
   void say();
   void hi();
}
class Cat implements IA{//一个普通类实现接口 就必须将该接口的所有方法都实现
   @Override
   public void say() {
   }
   public void hi(){
   }
}
abstract class Tiger implements IA{//抽象类实现接口 可以不用实现接口的方法
}
  1. 一个类同时可以实现多个接口class A extends B,C{}
  2. 接口中的属性 只能是final的 而且是 public static final修饰符 比如int a=1;实际上是 public static final int a=1;(必须初始化)
  3. 接口中属性的访问形式:接口名.属性名
  4. 接口不能继承其它的类 但是可以继承多个别的接口 比如interface A extends B,C{}
  5. 接口的修饰符只能是 public和默认 这点和类的修饰符是一样的

实现接口和继承类

当子类继承了父类 就自动的拥有父类的功能
如果子类需要扩展功能 可以通过实现接口的方式扩展 可以理解实现接口是对java单继承机制的一种补充

不同

  • 接口和继承解决的问题不同
    • 继承的价值主要在于:解决代码的复用性和可维护性
    • 接口的价值主要在于:设计好各种规范(方法) 让其它类去实现这些方法
  • 接口比继承更加灵活 继承是满足is-a的关系 而接口只需满足like-a的关系
  • 接口在一定程度上实现代码解耦 即接口规范性+动态绑定机制

接口的多态特征

  • 多态参数:接口可以指向实现了接口类的对象
  • 多态数组
  • 多态传递:接口类型的变量可以指向实现了该接口的类的对象 如果A类继承了B接口 而C类实现了A接口 实际上就相当于C类也实现了B接口
    image.png

内部类

定义

一个类的内部完整地嵌套了另一个类结构 被嵌套的类称为内部类 嵌套其他类的类称为外部类 是类的第五大成员(属性 方法 构造器 代码块 内部类)
语法:

class Outer{//外部类
    class Inter{//内部类
    }
}
class Other{//外部其他类    
}

分类

  • 如果定义类在局部位置(方法中/代码块):
    1. 局部内部类(有类名)
    2. 匿名内部类(没有类名)
  • 定义在成员位置:
    1. 成员内部类(没有static修饰)
    2. 静态内部类(有static修饰)

局部内部类的使用

  1. 可以直接访问外部类的所有成员 包含私有的
  2. 不能添加访问修饰符 因为它的地位就是一个局部变量 局部变量是不能使用修饰符的 但是可以使用final修饰 因为局部变量也可以使用final
  3. 作用域:仅仅在定义它的方法体或代码块中
  4. 局部内部类访问外部类的成员:直接访问
  5. 外部类访问局部内部类的成员:创建对象再访问(注意必须在作用域内)
  6. 外部其他类不能访问局部内部类 因为局部内部类地位是一个局部变量
  7. 如果外部类和局部内部类的成员重名时 默认遵循就近原则 如果想访问外部类的成员 则可以使用外部类名.this.成员访问 比如System.out.println("外部类的n2=”+外部类名.this.n2);
public class Test {
    public static void main(String[] args) {
        Outer outer=new Outer();
        outer.m1();
        System.out.println("Outer的hashcode="+outer);
        //n1=800外部类的n1=100
        //Other.this hashcode=Outer@6e8cf4c6
        //Outer m
        //Outer的hashcode=Outer@6e8cf4c6
    }
}
class Outer{//外部类
    private int n1=100;
    private void m2(){
        System.out.println("Outer m");
    }

    public void m1(){//方法
        final class Inter{//局部内部类
            private int n1=800;
            public void f1(){
                //局部内部类访问外部类的成员:直接访问
                //外部类和局部内部类的成员重名时 访问外部类的成员 则可以使用外部类名.this.成员
                System.out.println("n1="+n1+"外部类的n1="+Outer.this.n1);
                System.out.println("Other.this hashcode="+Outer.this);
                m2();
            }
        }
        //外部类在方法中 可以创建Inner对象 然后调用方法
        Inter inter=new Inter();
        inter.f1();
    }
}
class Other{//外部其他类
}

匿名内部类的使用

本质是类 内部类 该类没有名字 同时还是一个对象

  1. 语法:
new 类或接口(参数列表){  
    类体
}  
  1. p456

成员内部类

静态内部类

枚举和注解

p475