Java面向对象(下)
1. static关键字
static可以用来修饰类的内部结构(除了构造器):属性、方法、代码块、内部类
1.1 静态属性
- 用static修饰的属性为静态属性(类变量),非静态变量(或称实例变量)
- 实例变量:每个对象都有属于自己的一套非静态属性,互不影响
- 类变量:所有对象共用的。当通过一个对象修改静态变量,会导致其他变量调用此静态变量时,得到的是修改过的数值。
- 静态变量随着类的加载而加载,早于对象的创建。
而实例变量是在对象创建后才加载的
所以可以通过 “类.静态变量” 的方式进行调用 - 静态变量存储在方法区的静态域中
- 静态属性举例:System.out , Math.PI
class Chinese{
String name;
int age;
static nation;
}
class Test{
public static void main(String[] args){
//不用创建对象,就可以调用静态变量
//因为静态变量随着类的加载而加载
Chinese.nation = "CHINA";
}
}
1.2 静态方法
- 随着类的加载而加载,可以通过 类.方法 来调用方法
- 静态方法中只能调用静态的方法、静态的属性(由生命周期决定)
- 非静态方法既可以调用静态的,也可以调用非静态的
- 在静态的方法内,不能使用this,super关键字
1.3 什么时候声明为static
属性
- 该属性是被多个对象共用的,不会随着对象的不同而不同
- 类中的常量也常被修饰为static,如Math.PI
方法 - 操作静态属性的方法,通常为static
- 工具类中的方法,比如Math类、Arrays类、Collections类
2.单例设计模式
2.1 什么是设计模式
- 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格以及解决问题的思考方式。类似于棋谱。
- 设计模式是独立于语言的(和数据结构类似),对Java、Python等编程设计都适用
2.2 单例设计模式
- 单例设计模式的类只能由一个对象
实现方式
- 将构造器设置为private的(这样类的外部就不能创建对象了)
- 在类的内部创建静态的类对象instance
- 声明public的静态方法getInstance(),返回类内部创建的对象
2.3 饿汉式和懒汉式
//单例设计模式
//饿汉式
public class SingletonTest1 {
public static void main(String[] args) {
Bank b1 = Bank.getInstance();
Bank b2 = Bank.getInstance();
//地址值相同,说明这个类只有一个对象
System.out.println(b1 == b2);
}
}
class Bank {
//私有化类的构造器
private Bank() {
}
//内部创建类的对象
private static Bank instance = new Bank();
//声明public,static的getInstance方法,返回内部创建的类
public static Bank getInstance() {
return instance;
}
}
//单例模式
//懒汉式
public class SingletonTest2 {
public static void main(String[] args) {
Order order1 = Order.getInstance();
Order order2 = Order.getInstance();
System.out.println(order1 == order2);
}
}
class Order{
private Order(){
}
private static Order instance = null;
public static Order getInstance(){
if(instance == null){
instance = new Order();
}
return instance;
}
}
饿汉式和懒汉式的比较
- 饿汉式:
- 坏处:对象加载时间过长
- 好处:饿汉式是线程安全的
- 懒汉式:
- 好处:延迟对象的创建
- 目前的写法的坏处是线程不安全,到多线程时,可以修改
2.4 单例模式的使用场景
因为只生成一个对象,所以减少了系统的开销
[外链图片转存中…(img-KUsvedLP-1672233930225)]
3. main方法的理解
- main方法是程序的入口
- main方法也是一个普通的静态方法
- main()也可以作为我们与控制台交互的方式
4. 代码块(初始化块) 类的成员之四
- 代码块又称初始化块,作用是初始化类、对象
- 代码块如果有修饰的话,只能使用static
- 根据是否用static修饰分为:静态代码块、非静态代码块
class Person{
//非静态代码块
{
}
//静态代码块
static{
}
}
4.1 静态代码块
- 内部可以有输出语句
- 随着类的加载而执行,因而只会执行一次
- 可以初始化类的静态属性
- 如果一个类中定义了多个静态代码块,则先声明的静态代码块先执行
- 而所有的静态代码块的执行都先于非静态的代码块(因为静态代码块随着类的加载而执行,先于对象的创建)
- 静态代码块只能调用静态的结构
4.2 非静态代码块
- 内部可以有输出语句
- 随着对象的创建而执行,每创建一个对象就会执行一次非静态代码块
- 可以在创建对象的时候,对属性进行初始化
- 多个非静态代码块,执行顺序也是先声明先执行
- 非静态代码块既可以调用静态结构也可以调用非静态结构
5. 对属性赋值
数字表示赋值的先后顺序
- 默认初始化
- 显示初始化 代码块中初始化
哪个先写,哪个先赋值 - 构造器中初始化
- 使用对象.属性赋值
6.final 关键字
final —— 最终的(不可修改)
final可以用来修饰类、方法、变量
6.1 final修饰类
final类不能有子类,不能被继承
比如String类、System类、StringBuffer类
6.2 final修饰方法
表示此方法不能被重写
6.3 final修饰变量
final修饰变量,此时的变量就称为一个常量
- final修饰属性:可以赋值的位置:显示初始化、代码块初始化、构造器初始化
- final修饰局部变量:表示常量,只能调用,不能修改。
尤其是用final修饰形参时,在方法中只能使用,不能修改
6.4 static final
static final修饰属性:全局常量
7. abstract关键字
abstract 可以用来修饰类和方法
7.1 抽象类
抽象类不能实例化,只能被其他类继承
- 抽象类一定有构造器,便于子类实例化时调用
-
- 若子类重写了所有的抽象方法,则此子类可以实例化
-
- 否则,此子类也是抽象类,需要用abstract来修饰
7.2 抽象方法
抽象方法只有方法的声明,没有方法体
- 包含抽象方法的类一定时抽象类,反之抽象类中不一定有抽象方法
public abstract void eat();
注意点
- 什么时候用抽象类呢?
举个例子:几何图形这个类就是抽象的,计算面积这种方法只能声明,不能实现。 - abstract不能用来修饰私有方法、静态方法、final的方法、final的类
7.3 抽象类的匿名子类
- 一些抽象类因为有抽象方法,需要将抽象方法重写了才能创建对象
- 目的:匿名子类只需要使用一次(省事)
//假设Person是一个抽象类
//Person中eat方法是抽象方法
//创建匿名子类,把抽象类的抽象方法给实现了
//使用多态将子类对象赋给父类的引用
Person p = new Person(){
public void eat(){
System.out.println("你要好好吃饭啊");
}
}
7.4 模板方法设计模式
简单的说,像是填空题,把确定的写好了,不确定的暴露出去,让子类去实现
import java.time.format.TextStyle;
import javax.sound.midi.SysexMessage;
//模板方法设计模式
public class TemplateTest {
public static void main(String[] args) {
Template test = new SubTemplate();
test.spendTime();
}
}
//一个计算代码运行时间的类模板
abstract class Template{
public void spendTime(){
long start = System.currentTimeMillis();
code();
long end = System.currentTimeMillis();
System.out.println("代码运行的时间为"+(end - start));
}
public abstract void code();
}
class SubTemplate extends Template{
@Override
public void code() {
// TODO Auto-generated method stub
for(int i = 2;i<=1000;++i){
boolean isPrime = true;
for(int j = 2;j<=Math.sqrt(i);++j){
if((i%j==0)){
isPrime = false;
}
}
if(isPrime){
System.out.println(i);
}
}
}
}
8. 接口 interface
- 接口的本质是契约、标准、规范
- 继承表达的是"是不是"的关系(is a)(例如Student is a Person),而接口实现的是"能不能"的关系,比如这个电器能不能实现USB接口
- Java类不能多重继承,但是接口可以实现多重继承
- Java中类和接口的地位是并列的
8.1 接口的成员
- JDK7及以前
只能定义全局变量和抽象方法
- 全局常量:public static final,可以省略
- 抽象方法:public abstract,可以省略
- JDK8
除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
8.2 接口的实现和使用
- 接口中不能定义构造器,意味着接口不能实例化
- Java中,接口interface通过类class去实现(implements)的方法来使用
- 如果类覆盖了接口中的所有抽象方法,则此类就可以实例化
- 如果实现类没有覆盖接口中所有的抽象方法,则此类为抽象类
- Java类可以实现多个接口,不同接口之间用逗号隔开
- 接口的使用体现多态性
//格式
//先写继承,再写实现
class Plane extends Machine implements Flyable,Attackable{
}
接口和接口之间可以多继承
下面的接口C中有了A和B中的定义的抽象方法
interface A{
public abstract void method1();
}
interface B{
public abstract void method2();
}
interface C extends A,B{
}
接口的使用体现了多态性
/*
* 接口的使用体现多态
*/
public class USBTest {
public static void main(String[] args) {
Computer computer = new Computer();
// 接口多态性的体现
// 将实现了USB接口的Flash对象传给USB接口
computer.transfer(new Flash());
}
}
interface USB {
void start();
void end();
}
// 实现USB接口的Flash
class Flash implements USB {
public void start() {
System.out.println("Flash开始工作");
}
public void end() {
System.out.println("Flash结束工作");
}
}
class Computer {
public void transfer(USB usb) {
usb.start();
System.out.println("数据传输中....");
usb.end();
}
}
8.3 代理模式 接口的应用
//接口的应用:代理模式
interface NetWork {
void browse();
}
// 被代理类
class Server implements NetWork {
public void browse() {
System.out.println("真实的服务器访问");
}
}
// 代理类
class ProxyServer implements NetWork {
private NetWork work;
public ProxyServer(NetWork work) {
this.work = work;
}
public void check() {
System.out.println("联网前的检查");
}
public void browse() {
check();
work.browse();
}
}
public class NetWorkTest {
public static void main(String[] args) {
Server server = new Server();
ProxyServer proxyServer = new ProxyServer(server);
// 通过接口的多态性,在代理服务器的方法中调用了Server中的方法
proxyServer.browse();
}
}
8.4 JDK8接口新特性
还可以定义静态方法和默认方法
interface A{
public static void method1(){
System.out.println("接口中的静态方法");
}
public default void method2(){
System.out.println("接口中的默认方法");
}
}
- 接口中的静态方法,只能通过接口来调用,实现类不能用
//静态方法通过接口来调用,和工具类有点相似
A.method1();
- 默认方法可以通过实现类的对象调用,实现类还可以重写接口中的默认方法
- 类优先原则如果实现类(同时是子类)继承的父类和实现接口中声明了同名同参数的方法,那么子类在没有重写此方法的情况下。默认调用的是父类中的同名同参数的方法
- 如果多个接口中定义了同名同参的默认方法,实现类实现了多个接口,那么实现类会在没有重写该方法的情况下报错。这需要我们对方法进行重写
在实现类的方法中调用接口中默认方法
class SubClass implements A{
public void method2(){
System.out.println("Subclass:method2");
}
public void method3(){
//调用接口中默认方法
//接口名.super.默认方法名
A.super.method2();
}
}
9. 内部类 类的内部成员五
将一个类A声明在另一个类B中,A称为内部类,B称为外部类
内部类的分类
- 成员内部类(静态、非静态)
- 局部内部类
- 方法内
- 代码块内
- 构造器内
9.1 成员内部类
一方面,作为外部类的成员
- 可以调用外部类的结构
- 可以用static修饰
- 可以被4种权限修饰
另一方面,作为一个类
- 定义属性、构造器、方法、代码块
- 可以用final修饰,表示不能被继承;换个角度,不用final就可以被继承
- 可以用abstract修饰,表示不能被实例化
关注3个问题
- 如何实例化成员内部类的对象
- 如何在成员内部类中区分调用外部类的结构
- 开发中局部内部类的使用
//成员内部类的一些测试
public class InnerClass {
public static void main(String[] args) {
//静态内部类的实例化
Person.Dog dog = new Person.Dog();
dog.show();
//非静态内部类的实例化
Person p = new Person();
Person.Bird bird = p.new Bird();
bird.sing();
bird.display();
}
}
class Person{
String name = "小明";
//静态成员内部类
static class Dog{
String name = "大黄";
void show(){
System.out.println("小狗汪汪");
}
}
//非静态成员内部类
class Bird{
String name = "杜鹃";
void sing(){
System.out.println("小鸟唱歌");
}
void display(){
System.out.println(this.name); //调用内部类的属性
System.out.println(Person.this.name); //调用外部类的结构
}
}
}
局部内部类
class A {
// 局部内部类的使用
// 返回一个实现了Comparable接口的类的对象
public Comparable getComparable() {
// 局部内部类
class Mycomparable implements Comparable {
@Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
return 0;
}
}
return new Mycomparable();
}
}
- 在局部类中使用局部内部类所在方法中的局部变量时,要求局部变量是final的