当前位置: 首页 > news >正文

static、 静态导入、成员变量的初始化、单例模式、final 常量(Content)、嵌套类、局部类、抽象类、接口、Lambda、方法引用

static

static 常用来修饰类的成员:成员变量、方法、嵌套类

成员变量

  • static修饰:类变量、成员变量、静态字段
    • 在程序中只占用一段固定的内存(存储在方法区),所有对象共享
    • 可以通过实例、类访问 (一般用类名访问和修改,不建议用实例修改),但是不能通过类名访问实例变量和实例方法
  • 没有被static修饰:实例变量
    • 每个实例都有一份内存
package com.hxw.demo4;public class Person {public static  int count = 0;public int age;
}
package com.hxw.demo4;
public class Main {public static void main(String[] args) {Person.count = 20;Person p1 = new Person();p1.age = 20;System.out.println(p1.count);Person p2 = new Person();p2.age = 30;System.out.println(p2.count);}
}

控制台输出

20
20

方法

  • static修饰:类方法、静态方法
    • 可以通过实例、类访问
    • 内部不可以使用this
    • 可以直接访问类变量、类方法
    • 不可以直接访问实例变量和实例方法
  • 没有被static修饰:实例方法
    • 只能通过实例访问,不可以通过类访问
    • 内部可以使用this
    • 可以直接访问实例变量和实例方法
package com.hxw.demo4;public class Person {public static int count = 0;public int age;// 实例方法public void run1(){// 实例方法中可以使用this调用其他实例方法this.run2();//实例方法可以直接访问类变量和类方法System.out.println(count);test1();System.out.println(this.age);}public void run2(){System.out.println("void run2");}public static void test1(){System.out.println("static void test1");test2();System.out.println(count);}public static void test2(){System.out.println("static void test2");}
}
package com.hxw.demo4;public class Main {public static void main(String[] args) {Person person = new Person();person.age = 18;person.run1();Person.test1();}
}

总结:
在这里插入图片描述

静态导入

使用了静态导入之后,就可以省略类名来访问静态成员(成员变量、方法、嵌套类)

在这里插入图片描述

静态导入的经典应用

为了避免每一次都是用Math.PI,可以考虑使用静态导入,其他的Math方法也是一样的道理。
在这里插入图片描述

成员变量的初始化

  • 编译器会自动为未初始化的成员变量设置初始值
  • 如何手动给实例变量提供初始值
    • 在声明中:定义变量的时候直接给其设置初始值
    • 在构造方法中:在构造方法内部利用this进行初始化
    • 在初始化中 :这个情况看下面代码,了解一下,
package com.hxw.dmeo6;public class Person {public static int count;public int age = 1; //申明中public Person(){age = 20;// 在构造方法中,但是先执行初始化中的代码}//在初始化中{age = 18;}
}

一般很少使用初始化代码,方法如下 公共初始化代码一般放到参数最多的构造方法中

package com.hxw.dmeo6;public class Person {public static int count;public int age = 1; //申明中public Person(){this(0, 0);}public Person(int age){this(age, 0);}public Person(int age, int no){// 公共初始化代码一般放到参数最多的构造方法中}//    //在初始化中
//    {
//        age = 18;
//    }
}
  • 如何手动给类变量提供初始值
    • 在声明中
    • 在静态初始化代码块中:当一个类初始化的时候(也就是一个类第一次被主动使用时,JVM会对类进行初始化),就会执行静态初始化代码块,且只会执行一次。
      • 可以有多个静态初始化块,按照在源码中出现的顺序被执行

在这里插入图片描述

初始化块和静态初始化块

这里实际的执行顺序和自己想的不一样,要先初始化完父类,再初始化子类
在这里插入图片描述

单例模式

如果一个类设计为单例模式,那么程序运行过程,这个类只能创建一个实例

  • 饿汉单例模式:一开始就创建了对象
  • 懒汉单例模式:等到该创建对象的时候创建对象,懒汉单例模式会有线程安全问题如果三个对象同时构建实例,那么会创建多个对象,但是最终创建的才会赋值给instance,所以总的说来还是单例模式

Rocket.class

package com.hxw.demo7;/*** 饿汉式单例模式:一上来就new了一个对象*/
//public class Rocket {
//    //私有的静态(唯一内存)实例变量
//    private static Rocket instance = new Rocket();
//
//    // 构造方法私有化 在Main中就不能new Rocket()
//    private Rocket(){
//
//    }
//
//    //公共的静态方法,返回唯一的静态实例
//    public static Rocket getInstance(){
//        return instance;
//    }
//}/*** 懒汉式单例模式*/public class Rocket {public static Rocket instace = null;private Rocket(){}public static Rocket getInstance(){//用到的时候再new对象if(instace == null){instace = new Rocket();}return instace;}
}

Main.class

package com.hxw.demo7;public class Main {public static void main(String[] args) {Rocket r1 = Rocket.getInstance();Rocket r2 = Rocket.getInstance();Rocket r3 = Rocket.getInstance();//r1 r2 r3 都是同一个instanceSystem.out.println(r1 == r2);System.out.println(r2 == r3);}
}

控制台输出

true
true

final和常量

  • final修饰的类不能被继承
  • final修饰的成员方法不能被重写
  • final修饰的变量,只能进行一次赋值,而且需要初始化,如果定义的时候没有初始化,那么在构造函数中一定要初始化

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

常量(Content)

  • 常量的写法: public static final double PI = 3.14159265358979323846;,一般名字全部用大写
  • 如果将基本类型或字符串定义为常量,并且在编译时就能确定值,编译器就会使用常量值代替各处的常量名(类似于C语言的宏替换),这样可以减少访问内存的次数,提高代码运行的效率

嵌套类

内部类(Inner Class,非静态嵌套类)

  • 嵌套类:定义在另一个类中的类
  • 外部类:在嵌套类外层的类
  • 顶级类:最外层的外部类
  • 内部类:没有被static修饰的嵌套类,非静态嵌套类。
    • 跟实例变量、实例方法一样,内部类与外部类的实例相关联**,必须先创建外部类实例,然后再用外部类实例创建内部类实例**。
    • 内部类不能定义除了除编译时常量以外的任何static成员
    • 内部类可以直接访问外部类中的所有成员(即使被申明为static)因为先创建了外部类实例,再创建内部类实例。
    • 外部类可以直接访问内部类实例的成员变量、方法
      外部类、内部类如下
package com.hxw.demo9;public class OuterClass {// 静态嵌套类static class StaticNestedClass {}// 非静态嵌套类(内部类)class InnerClass{}class A{class B{class C{}}}}

在这里插入图片描述
对应的内存图如下
在这里插入图片描述

静态嵌套类

  • 静态嵌套类:被static修饰的嵌套类
  • 静态嵌套类在行为上就是一个顶级类,只是定义的代码写在了另一个类中
  • 对比一般的顶级类,静态嵌套类多了一些特殊权限
    • 可以访问外部类中的成员(即使被申明为static)

在这里插入图片描述
在这里插入图片描述

什么情况使用嵌套类

情况一:如果类A只用在类C内部,可以考虑将类A嵌套到类C内部

  • 封装性更好
  • 程序包更加简化
  • 增强可读性、维护性

情况二:如果类A需要经常访问类C的非公共成员,可以考虑将类A嵌套到类C中

  • 另外也可以根据需要将类A隐藏起来,不对外暴露

情况三:如果需要经常访问非公共的实例成员(而且需要现有外层,后有内层的关系,例如现有公司Company 后有员工Employee),设计成内部类(非静态嵌套类),否则设计成静态嵌套类
- 如果必须现有C实例,才能创建A实例,那么可以将A设计为C的内部类

情况一案例:
在这里插入图片描述
在这里插入图片描述

局部类

  • 局部类:定义在代码块(包含{},可以在里面写代码的块)中的类(可以定义在方法中,for循环中,if语句中)
  • 局部类不能定除编译时常量以外的任何static成员
  • 局部类只能访问final或者有效final(只赋值一次,没有被第二次赋值)的局部变量
  • 局部类可以直接访问外部类的所有成员
    • 局部类只有定义在实例相关的代码块中,才能直接访问外部类中的实例成员(实例变量、实例方法)

由于局部类是定义在代码块中的,所以其作用域也只局限于代码块中,因此,什么时候会用到局部类呢?当一个类是在一个代码块中使用的时候

package com.hxw.demo10;public class Person {static {class A{}}{int age = 10;class A{}}public void test(){for (int i = 0; i < 10; i++) {class A{}}if(true){class A{}}else{class A{}}{class A{}}}
}

在这里插入图片描述

抽象类

抽象方法(Abstract Method)

  • 抽象方法:被abstract修饰的方法
    • 只有方法申明,没有方法实现
    • 不能是private权限(因为定义抽象方法的目的就是为了让子类去实现
    • 只能是实例方法,而不能是类方法
    • 只能定义在抽象类,接口中
package com.hxw.demo11;public abstract class Main {public static void main(String[] args) {}public abstract void test();
}

抽象类

  • 抽象类:被abstract修饰的类
    • 可以定义抽象方法
    • 不能实例化,但是可以自定义构造方法
    • 子类必须实现抽象父类中的所有抽象方法(除非子类也是一个抽象类)
    • 可以向非抽象类一样定义成员变量、常量、嵌套类型、初始化块、非抽象方法等,也就是,抽象类也可以完全不定义抽象方法

常见使用场景

  • 抽取子类的公共实现到抽象父类中,要求子类必须要单独实现的去定义成抽象方法

接口(Interface)

联想到日常笔记本电脑的接口,不同厂商的USB长得都是一样的,这是他们遵守相同的标准

  • API(Application Programming Interface

    • 应用编程接口,提供给开发者调用的一组功能(无需提供源码)
  • java中的接口

    • 一系列方法申明的集合
    • 用来定义规范、标准

抽象类(abstract)是通过子类继承来实现其抽象方法,接口(Interface)是通过其他类实现该接口来实现其抽象方法

家教案例引入接口概念

有一个小孩,需要应聘家教,应聘要求:

  • 会教授编程
  • 会教踢足球
    涉及的人群可能有:在校的学生,在校的老师、出租车司机

首先看没有使用接口:

  • 定义一个孩子类
package com.hxw.demo12;public class Child {private String name;private Student jiajiao;public Child(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Student getJiajiao() {return jiajiao;}public void setJiajiao(Student jiajiao) {this.jiajiao = jiajiao;}public void study(){jiajiao.jiaoBianCheng(this);jiajiao.jiaoZuQiu(this);}}
  • 定义一个学生类
package com.hxw.demo12;public class Student {public void jiaoBianCheng(Child child){System.out.println("Student教" + child.getName() + "编程");}public void jiaoZuQiu(Child child){System.out.println("Student教" + child.getName() + "足球");}}
  • 主函数
package com.hxw.demo12;public class Main {public static void main(String[] args) {Child child = new Child("Jack");child.setJiajiao(new Student());child.study();}
}

如果希望老师教学生的话,按照这种方式就需要再创建一个Teacher类…教课的人身份众多,都需要单独修改Child类中的jiajiao属性,以及本类中的方法,这样很繁琐,但是这些类本质上都是实现了两个方法,招人的要求是满足这两个要求,但是对于这个人其他的特征,其实不用了解。此时,就可以将这两个行为定义到接口当中,各种身份的家教去实现这个接口即可。

  • 创建JiaJiaoable接口,将原来Student中的方法复制过去,并改写为抽象方法。
package com.hxw.demo12;public interface JiaJiaoable {public abstract void jiaoBianCheng(Child child);public abstract void jiaoZuQiu(Child child);
}

Student类、Teacher类分别实现JiaJiaoable接口中的方法

package com.hxw.demo12;public class Student implements JiaJiaoable{@Overridepublic void jiaoBianCheng(Child child) {System.out.println("Student教" + child.getName() + "biancheng");}@Overridepublic void jiaoZuQiu(Child child) {System.out.println("Student教" + child.getName() + "zuqiu");}
}
package com.hxw.demo12;public class Teacher implements JiaJiaoable{@Overridepublic void jiaoBianCheng(Child child) {System.out.println("Teacher教" + child.getName() + "biancheng");}@Overridepublic void jiaoZuQiu(Child child) {System.out.println("Teacher教" + child.getName() + "zuqiu");}
}

修改孩子类的家教属性为借口类型

package com.hxw.demo12;public class Child {private String name;private JiaJiaoable jiajiao;public Child(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public JiaJiaoable getJiajiao() {return jiajiao;}public void setJiajiao(JiaJiaoable jiajiao) {this.jiajiao = jiajiao;}public void study(){jiajiao.jiaoBianCheng(this);jiajiao.jiaoZuQiu(this);}}

调用Main方法:

package com.hxw.demo12;public class Main {public static void main(String[] args) {Child child = new Child("Jack");child.setJiajiao(new Student());child.study();child.setJiajiao(new Teacher());child.study();}
}

控制台输出

Student教Jackbiancheng
Student教Jackzuqiu
Teacher教Jackbiancheng
Teacher教Jackzuqiu

接口中可以定义的内容

  • 可以定义:抽象方法、常量、嵌套类型,从java8开始定义
    • 上述可以定义的内容都是隐式public的,因此可以省略public关键字
    • 从java9开始可以定义private方法
  • 常量可以省略staticfinal
    • 接口中没有成员变量,写出来就是常量
  • 抽象方法可以省略abstract
  • 不能自定义构造方法、不能定义初始化块、不能实例化

接口的细节

  • 接口名称可以在任何使用类型的地方使用
  • 一个类可以通过implement关键字实现一个或者多个接口
    • 实现接口的类必须接口中定义的所有抽象方法,除非它也是一个抽象类
    • 如果一个类实现的多个接口中有相同的抽象方法,只需要实现此方法一次
    • extendsimplement可以同时使用,但是implements必须写在extends后面
    • 当父类、接口中的方法签名(方法名称和参数列表)一样,那么返回值类型也必须一样

一个接口可以通过extends关键字继承一个或者多个接口

  • 当多个父接口方法签名一样的时候,那么返回值类型也必须一样

定义保姆类

package com.hxw.demo12;public interface BaoMuable {void cook(Child child);
}

学生不仅想应聘家教,也想应聘保姆

package com.hxw.demo12;public class Student implements JiaJiaoable, BaoMuable{@Overridepublic void jiaoBianCheng(Child child) {System.out.println("Student教" + child.getName() + "biancheng");}@Overridepublic void jiaoZuQiu(Child child) {System.out.println("Student教" + child.getName() + "zuqiu");}@Overridepublic void cook(Child child) {}
}

由上述案例,可以看出 一个类可以通过implement关键字实现一个或者多个接口

定义一个Animal类

package com.hxw.demo12.test;public class Animal {public void eat(String name){}
}

定义一个吃东西的接口

package com.hxw.demo12.test;public interface Eatable {void eat(String name);
}

定义了一个小狗类,这个类继承了Animal.class并且实现了Eatable接口

package com.hxw.demo12.test;public class Dog extends Animal implements Eatable{@Overridepublic void eat(String name) {}public static void main(String[] args) {Dog dog = new Dog();dog.eat("bone");}}

抽象类和接口的对比

抽象类和接口的用途有些类似,该如何选择?

抽象类:
1. 继承:A extends D (A 是 D)接口:
2.实现:A implement D(A会D中的行为)
  • 何时选择抽象类
    • 在紧密相关的类之间共享代码(例如人、老师都吃东西)
    • 需要除public以外的访问权限
    • 需要定义实例变量、非finial的静态变量(接口中只能定义常量)
  • 何时选择接口
    • 不相关的类实现相同的方法 (例如人、狗都吃东西)
class Student{public void eat(){// 代码1// 代码2}
}
class Teacher{public void eat(){// 代码1// 代码2}
}
StudentTeacher中的eat()代码一样,就可以将其共享出来,选择抽象类

接口的升级问题

  • 如果接口需要升级,比如增加新的抽象方法
    • 会导致大幅度的代码改动,以前实现的接口类都得改动
  • 若想在不改动以前实现类的前提下进行接口升级,从java8开始有两种方案
    • 默认方法(Default Method):将接口中新增的方法定义为默认方法,默认方法是允许有实现的
    • 静态方法(Static Method)

Lambda

Lambda Expression

  • Lambda 表达式是从java8才开始有的语法。
  • 函数式接口(Funxtional Interface):只包含一个抽象方法的接口
    • 可以在接口上加上@FunctionalInterface注解,表示其是一个函数式接口
  • 当匿名类实现的是函数式接口时, 可以使用Lambda表达式进行简化

函数式接口

Lambda注意事项

  • Lambda只能访问finial或者有效finial的局部变量
  • Lambda没有引入新的作用域

定义一个OuterClass

package com.hxw.demo13;public class OuterClass {private int age = 1;public class InnerClass{private int age = 2;void inner(){Testable t = v -> {System.out.println(v);System.out.println(age);System.out.println(this.age);System.out.println(InnerClass.this.age);System.out.println(OuterClass.this.age);};t.test(3);}}public static void main(String[] args) {new OuterClass().new InnerClass().inner();}
}

方法引用

  • 如果Lambda中的内容仅仅是调用这个方法,可以使用方法引用(method Reference)来简化

http://www.mrgr.cn/news/59561.html

相关文章:

  • 15分钟学 Go 第 19 天:接口的实现
  • vue3中计算属性的用法以及使用场景
  • PHP+REDIS设置请求限流(设置1秒内最大请求数1000QPS)
  • 大学新生如何入门编程:选择语言、制定计划及避开学习陷阱
  • 鸿蒙中富文本编辑与展示
  • 蓝桥杯题目理解
  • 2024 BuildCTF 公开赛|MISC
  • Redis高频面试题
  • 【C++】—— 模板进阶
  • 十三、层次式架构设计理论与实践
  • 为制造业挑选CRM?11款软件对比指南
  • spring高手之路
  • 使用沉浸式翻译插件来使用多种人工智能工具翻译网页上的某段文字,如何做?
  • yolov5将推理模型导出为onnx
  • 字节青训营 红包运气排行榜
  • 初始JavaEE篇——多线程(4):生产者-消费者模型、阻塞队列
  • 【无人机设计与控制】改进人工势场法,引入模糊控制实现无人机路径规划和避障
  • mongodb:增删改查和特殊查询符号手册
  • 探索Python安全字符串处理的奥秘:MarkupSafe库揭秘
  • 轻松构建高效 API:FastAPI 的主要特点与实战应用20241027
  • Spring Boot技术在学生宿舍管理系统中的创新
  • 【工具使用】VSCode如何将本地项目关联到远程的仓库 (vscode本地新项目与远程仓库建立链接)
  • C语言初阶:十.结构体基础
  • 使用 Docker 管理完整项目:Java、Vue、Redis 和 Nginx 的一站式部署
  • ubuntu20上部署gitlab并开启ipv6访问
  • 【Java Maven框架】