Java面向对象(下)--static/final/代码块/抽象/接口/内部类
目录
1 关键字:static
2 理解main方法的语法
3 类的成员之四:代码块
4关键字:final
5 抽象类与抽象方法
6 接口(interface)
7 类的成员之五:内部类
static
功能
-
含义:静态的
-
可以用来修饰属性、方法、代码块、内部类
-
使用static修饰属性:表示静态变量(类变量),没有static的为实例变量,如果创建了类的多个对象,多个对象共享同一个静态变量,当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时是修改过的
静态变量的其他说明:
- 静态变量随着类的加载而加载,早于对象的创建,和类一样只会加载一次,在内存中存在一份
- 可以通过类.静态变量的方式进行调用,而实例变量不能这样调用
- 内存解析:
- 静态方法:类似静态变量,可以通过类之间调用,随着类加载,只能调用静态的方法或属性,对于非静态方法中,既可以调用非静态方法或属性,也可以调用静态方法或属性
- 注意点:
- 静态的方法内,不能使用this关键字、super关键字
- 关于静态属性和静态方法的使用,可以从生命周期的角度理解
- 开发中,如何确定一个属性是否要声明为static?
-
如果属性是可以被多个对象所共享的,不会随着对象的不同而不同,如银行账户类中的利率属性
-
类中的常量也常常声明为static
开发中,如何确定一个属性是否要声明为static?
-
操作静态属性的方法,通常设置为static
-
工具类中的方法,习惯声明为static。比如Math、Arrays、Collections,不需要new对象即可直接用类调用方法
应用
class Circle{
private double radius;
private int id;
public Circle(){
id=init++;//可以实现每次创建自动生成连续的id
total++;
}
public Circle(double radius){
this();//先调用一次上面的构造器,不用重复
this.radius=radius;
}
private static int init=1001;
private static total;
}
单例设计模式
采取一定的方法保证整个软件系统中某个类只存在一个对象实例,且该类只提供一个取得其对象实例的方法
饿汉式
public class SingletonTest1{
public static void main(String[] args){
Bank bank1=Bank.getInstance();
Bank bank2=Bank.getInstance(); //此时bank1==bank2成立
}
}
class Bank{
//1.私有化类的构造器
private Bank(){
}
//2.内部创建类的对象,要求此对象声明为静态
private static Bank instance=new Bank();
//3.提供公共方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}
懒汉式
public class SingletonTest1{
public static void main(String[] args){
Order order1=Bank.getInstance();
Order order2=Bank.getInstance(); //此时bank1==bank2成立
}
}
class Order{
//1.私有化类的构造器
private Order(){
}
//2.内部创建类的对象,要求此对象声明为静态
private static Order instance=null;
//3.提供公共方法,返回类的对象
public static Order getInstance(){
if(instance==null){
instance=new Order();
}
return instance;
}
}
区分饿汉式和懒汉式
饿汉式:对象加载时间过长(缺点),是线程安全的(优点)
懒汉式:延迟对象的创建(优点),目前的写法线程不安全,多线程内容时可以修改
使用场景
只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多资源时,可以直接产生一个单例对象,永久驻留内存
- 网站计数器,为了保证同步
- 应用程序的日志应用,因为日志文件一般一直处于打开操作,便于追加
- 数据库连接池,一个池子可以同时进行n个连接, 连接池固定一个
- 读取配置文件的类
- Application类
- windows任务管理器、回收站等
理解main方法的使用
- main()方法作为程序的入口
- main()也是一个普通的静态方法
- 可以作为我们与控制台交互的方式,之前是使用Scanner
类的成员四:代码块
相当于对属性赋值多了一种方式,实际上使用频率不高
- 代码块作用:用于初始化类、对象
- 代码块如果有修饰,只能使用static
- 分类:静态代码块 非静态代码块
- 静态代码块:
- 内部可以有输出语句
- 随着类的加载而执行;
- 用于初始化类的信息;
- 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行;
- 静态代码块的执行要优先于非静态代码块的执行
- 非静态代码块:
- 内部可以有输出语句
- 随着对象的创建(new)而执行
- 每创建一个对象就执行一次
- 作用是可以在创建对象时,对对象的属性等进行初始化
- 如果一个类中定义了多个非静态代码块,则按照声明先后顺序执行
- 属性赋值的执行顺序
默认初始化--显式初始化/代码块中赋值--构造器中初始化--对象.属性或对象.方法赋值
final关键字
- final可以修饰的结构:类、方法、变量
- final类:此类不能被其他类继承,如String、System、StringBuffer
- final方法:不允许被重写,比如Object中getClass
- final变量:此时的变量称为是一个常量,不允许改变
- 允许的赋值位置有:显示初始化,代码块中初始化、构造器中初始化
- final局部变量:尤其是使用final修饰形参时,表明此形参是常量,调用此方法时给该形参赋值,之后方法中只能使用不能重新赋值
- static final修饰属性:全局常量,接口中属性全是全局常量
- 用途:一般方法不常使用,属性有时需要final
抽象类与抽象方法
abstract意为抽象的,用来修饰结构、类、方法
抽象类
abstract修饰类:抽象类,此类不能实例化
- 抽象类中一定有构造器,便于子类实例化时调用
- 一般开发中都会提供子类
抽象方法
- 只有方法的声明,没有方法体
- 包含抽象方法的类必须是抽象类,但抽象类中可以没有抽象方法
- 若子类重写了父类所有抽象方法后,子类方可实例化
abstract class Person{
public abstract void eat();
}
使用上的注意点
- abstract不能用来修饰属性、构造器等结构
- 不能用来修饰私有方法、静态方法、final方法
创建抽象类的匿名子类
//Person定义为抽象类,Worker/Student为Person的子类
Worker worker=new Worker();
method(worker);//非匿名类非匿名对象
method(new Student()); //非匿名类匿名对象
//创建了一各匿名子类的非匿名对象:p
Person p=new Person(){
@override
public void eat(){//抽象类的子类必须重写其中的抽象方法
}
}
//创建匿名子类的匿名对象
method(new Person(){
@override
public void eat(){//抽象类的子类必须重写其中的抽象方法
}
})
模板方法设计模式
public class TemplateTest{
SubTemplate t=new SubTemplate();
t.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(){
System.out.println("sss");
})
}
}
接口
- 有时需要从几个类派生出一个子类,java不支持多重继承,类无法满足这个条件。
- 有时需要从几个类提取共同行为特征,但又没有is-a关系,只是有相同的行为特征。如大学生中学生都是学生,跨栏运动员篮球运动员都是运动员,他们都具有学习的技能,一方面他们已经有父类,另一方面他们与技能不满足子父类关系,因此可以把技能定义为接口
接口的使用
-
接口使用interface来定义
-
java中,接口和类是并列的两个结构
-
如何定义接口:定义接口中的成员
3.1 JDK7及以前:接口中只能定义全局常量和抽象方法
全局常量:
public static final
(即使省略这几个词依然是全局常量)抽象方法:
public abstract
(即使省略这几个词依然是抽象方法)接口中不能定义构造器!!意味着接口不可以实例化
3.2 JDK8以后 除了全局常量和抽象方法还可以定义静态方法和默认方法
-
Java开发中,接口通过让类去实现(Implements)的方法来使用
如果实现类重写了接口中所有抽象方法,则此实现类可以实例化
如果没有覆盖所有抽象方法,则此实现类仍为抽象类
-
java类可以实现多个接口,打破了类的单继承性的局限性,多个接口用
implements A,B
即可 -
接口与接口间可以继承,且可以多继承
-
接口的具体使用体现的多态性,接口实际上可以看做一种规范
package com.atguigu.ex1;
public class test {
}
interface Flyable{
public static final int MAX_SPEED=7900;
int MIN_SPEED=7900;
public abstract void fly();
void stop();
}
//重写全部抽象方法 可以实例化
class plane implements Flyable{
@Override
public void fly() {
System.out.println("起飞");
}
@Override
public void stop() {
System.out.println("停止");
}
}
//未重写全部抽象方法 必须为抽象类
abstract class Kite implements Flyable{
@Override
public void fly() {
// TODO Auto-generated method stub
}
}
//接口间多继承
interface AA{
}
interface BB{
}
interface CC extends AA,BB{
}
代理模式
应用场景:
安全代理:屏蔽对真实角色的直接访问
远程代理:通过代理类处理远程方法调用
延迟加载:先加载轻量级代理对象,需要再加载真实对象
分类:
静态代理
动态代理
工厂设计模式
实现了创建者与调用者的分离,即将创建对象的具体过程屏蔽起来,达到提高灵活性的目的
简单工厂模式,定义一个实现类对象的工厂类XXXFactory,包含getXXX等功能但是增加产品时需要对现有代码进行修改,违反了开闭原则
工厂方法模式,定义一个工厂接口,N个工厂类实现工厂接口,即可
Java8接口新特性
除了定义全局常量和抽象方法以外,还可以定义静态方法、默认方法
静态方法
1.接口中定义的静态方法,只能通过接口调用
2.通过实现类的对象,可以调用接口中的默认方法
3.如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,那么子类没重写时,默认调用父类该方法(类优先原则)
但属性不允许重名
4.如果实现类实现了多个接口,而多个接口中定义了同名同参数默认方法,那么实现类没有重写此方法时报错(接口冲突),这就需要在实现类中重写此方法
public class SubclassTest {
public static void main(String[] args) {
Subclass s=new Subclass();
//s.method1(); 接口的实现类调用会报错
//1.接口中定义的静态方法,只能通过接口调用
CompareA.method1();
//2.通过实现类的对象,可以调用接口中的默认方法
s.method2();
s.method3();
}
}
package com.atguigu.ex1;
public interface CompareA{
public static void method1() {
System.out.println("method1");
}
public default void method2() {
System.out.println("method1");
}
default void method3() {
System.out.println("method1");
}
}
内部类
一个类定义于另一个类的内部,称为内部类,反之为外部类
- 内部类一般用在定义它的类或语句块内,外部引用时必须给出完整的名称
- 内部类分类:成员内部类(静态、非静态) 局部内部类(方法内、代码块内、构造器内)
- 成员内部类:可以调用外部类的结构;可以定义属性、方法、构造器等;可以被final、abstract修饰
关注三个问题:
- 如何实例化成员内部类的对象
//创建Brain实例 静态的成员内部类
Person.Brain br=new Person.Brain();
//创建Eye实例 非静态的成员内部类
Person person=new Person();
Person.Eye eye=person.new Eye();
- 如何在成员内部类中区分调用外部类的结构
如果Person有内部类Brain,都定义了name属性,且Brain中有个方法形参为name,则
name 形参
this.name Brain类中的
Person.name Person中的
- 开发中局部内部类的使用
面试题:抽象类与接口异同
相同点:
- 不能实例化
- 都可以被继承
不同点:
- 抽象类有构造器,接口不能声明构造器
- 抽象类只能单继承,接口可以多继承