面向对象下

这一章主要涉及其他关键字,包括 this、super、static、final、abstract、interface、package、import 等。

static

在 Java 类中,可用 static 修饰属性、方法、代码块、内部类。

特点:

  • 随着类的加载而加载,由于类只会加载一次,则静态变量在内存中也只会存在一份,存在方法区的静态域中;
  • 优先于对象存在;
  • 修饰的成员,被所有对象所共享;
  • 访问权限允许时,可不创建对象,直接被类调用。

注意

  • 在静态的方法中,不能使用 this 关键字和 super 关键字;
  • 关于静态属性和静态方法的使用,要从生命周期的角度去理解。

单例模式

所谓的单例模式,就是采取一定的方法保证在整个系统中,对某个类只能存在一个对象实例。

单例模式的好处:

  • 某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销;
  • 省去了 new 操作符,降低了系统内存的使用频率,减轻了 GC 压力;
  • 保证独立性等。

实现:

对比:

  • 饿汉式的坏处是对象加载时间过长,好处是线程安全的;
  • 懒汉式的好处是延迟对象的加载,坏处是线程不安全的;

应用:

  • 网站的计数器,一般也是单例模式实现,否则难以同步;
  • 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志

    文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加;
  • 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库

    资源;
  • 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置

    文件数据,都生成一个对象去读取;
  • Application 也是单例的典型应用;
  • Windows 的 Task Manager (任务管理器) 就是很典型的单例模式;
  • Windows 的 Recycle Bin (回收站) 也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

理解 main 方法的语法

由于 JVM 需要调用类的 main() 方法,所以该方法的访问权限必须是

public,又因为 JVM 在执行 main() 方法时不必创建对象,所以该方法必须是 static 的,该方法接收一个 String 类型的数组参数,该数组中保存执行 Java命令时传递给所运行的类的参数

又因为 main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创

建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员。

代码块

代码块(或初始化块)的作用:对 Java 类或对象进行初始化。

一个类中代码块若有修饰符,则只能被 static 修饰,称为静态代码块

(static block),没有使用 static 修饰的,称为非静态代码块。

静态代码块:用 static 修饰的代码块

  • 可以有输出语句;
  • 可以对类的属性、类的声明进行初始化操作;
  • 不可以调用非静态的属性和方法;
  • 若有多个静态代码块,那么按照从上到下的顺序依次执行;
  • 静态代码块随着类的加载而加载,且只执行一次。静态代码块的执行要先于非静态代码块。

非静态代码块:没有 static 修饰的代码块

  • 可以有输出语句;
  • 可以对类的属性、类的声明进行初始化操作;
  • 可以调用非静态和静态的属性和方法;
  • 若有多个非静态代码块,那么按照从上到下的顺序依次执行;
  • 每次创建对象的时候,都会执行一次。非静态代码块的执行要先于构造器。
package com.parzulpan.java.ch04;

/**
* @Author : parzulpan
* @Time : 2020-11-22
* @Desc : 代码块练习
*/ class Root{
static{
System.out.println("Root的静态初始化块"); // 1
} {
System.out.println("Root的普通初始化块"); // 4
} public Root(){
System.out.println("Root的无参数的构造器"); // 5
}
} class Mid extends Root{
static{
System.out.println("Mid的静态初始化块"); // 2
} {
System.out.println("Mid的普通初始化块"); // 6
} public Mid(){
System.out.println("Mid的无参数的构造器"); // 7
} public Mid(String msg){
//通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数构造器,其参数值:"
+ msg); // 8
}
} class Leaf extends Mid{
static{
System.out.println("Leaf的静态初始化块"); // 3
} {
System.out.println("Leaf的普通初始化块"); // 9
} public Leaf(){
//通过super调用父类中有一个字符串参数的构造器
super("尚硅谷");
System.out.println("Leaf的构造器"); // 10
}
} public class LeafTest{
public static void main(String[] args){
new Leaf(); // 输出结果顺序看注释
}
}
package com.parzulpan.java.ch04;

/**
* @Author : parzulpan
* @Time : 2020-11-22
* @Desc : 代码块练习
*/ class Father {
static {
System.out.println("11111111111"); // 1
} {
System.out.println("22222222222"); // 2
} public Father() {
System.out.println("33333333333"); // 3 } } public class Son extends Father {
static {
System.out.println("44444444444"); // 4
} {
System.out.println("55555555555"); // 5
} public Son() {
System.out.println("66666666666"); // 6
} public static void main(String[] args) { // 由父及子 静态先行
System.out.println("77777777777"); // 7
System.out.println("************************"); // 8
new Son(); // 输出结果:1 -> 4 -> 7 -> 8 -> 2 -> 3 -> 5 -> 6
System.out.println("************************"); new Son(); // 输出结果:2 -> 3 -> 5 -> 6
System.out.println("************************");
new Father(); // 输出结果:2 -> 3
} }

总结:由父及子,静态先行。

程序中成员变量赋值的执行顺序:

  • 声明成员变量的默认初始化;
  • 显式初始化、多个初始化块一次被执行(同级别下按先后顺序执行);
  • 构造器再对成员进行初始化操作;
  • 通过“对象.属性”或“对象.方法”的方式,可多次给属性赋值。

final

在 Java 中声明方法变量时,可使用关键字 final 来修饰,表示“最终的”。

  • final 标记的类不能被继承。比如 String类、System类、StringBuffer类;
  • final 标记的方法不能被子类重写。比如 Object 类中的 getClass()
  • final 标记的变量(成员变量或局部变量)称为常量(名称大写,且只能被赋值一次)。比如 final double MY_PI = 3.14;

注意:final 标记的成员变量必须 在声明时在每个构造器中代码块中 显式赋值,然后才能使用。

static final 用来修饰属性,则称为全局常量。

抽象类与抽象方法

  • 用 abstract 关键字修饰一个类,这个类叫做抽象类;

    • 抽象类不能被实例化;
    • 抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体,此时才可实例化。若没有重写全部的抽象方法,仍为抽象类,需要使用 abstract 修饰;
    • 抽象类中一定有构造器,便于子类实例化调用(涉及子类对象实例化的全过程)。
  • 用 abstract 关键字修饰一个方法,这个方法叫做抽象方法;
    • 抽象方法:只有方法的声明,没有方法的实现,以分号结束。比如:public abstract void talk();
    • 含有抽象方法的类必须被声明为抽象类。

注意

  • 不能用 abstract 修饰变量、代码块、构造器;
  • 不能用 abstract 修饰私有方法、静态方法、final 的方法、final 的类。
package com.parzulpan.java.ch04;

/**
* @Author : parzulpan
* @Time : 2020-11-22
* @Desc : 抽象类举例
*/ public class AbstractTest {
public static void main(String[] args) {
AA aa = new BB();
aa.m1();
aa.m2();
}
} abstract class AA {
abstract void m1(); public void m2() {
System.out.println("A类中定义的m2方法");
}
} class BB extends AA {
void m1() {
System.out.println("B类中定义的m1方法");
}
}

模版方法模式

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

解决的问题:

  • 当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
  • *换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
package com.parzulpan.java.ch04;

/**
* @Author : parzulpan
* @Time : 2020-11-22
* @Desc : 抽象类的应用:模版方法模式
*/ public class TemplateMethodTest2 {
public static void main(String[] args) {
BankTemplateMethod bankTemplateMethod = new DrewMoney();
bankTemplateMethod.process();
BankTemplateMethod bankTemplateMethod1 = new ManageMoney();
bankTemplateMethod1.process();
} } abstract class BankTemplateMethod { private int id;
private static int init = 0; BankTemplateMethod() {
super();
id = init++;
} // 具体方法
public void takeNumber() {
System.out.println("取号排队 " + this.id);
}
public void evaluate() {
System.out.println("反馈评分\n");
} // 钩子方法,办理具体的业务
public abstract void transact(); // 模版方法,把基本操作组合到一起,子类一般不能重写
public final void process() {
this.takeNumber();
this.transact();
this.evaluate();
}
} class DrewMoney extends BankTemplateMethod {
@Override
public void transact() {
System.out.println("我要取款");
}
} class ManageMoney extends BankTemplateMethod {
@Override
public void transact() {
System.out.println("我要理财");
}
}

模板方法设计模式是编程中经常用得到的模式。各个框架、类库中都有他的影子,比如常见的有:

  • 数据库访问的封装;
  • Junit 单元测试;
  • JavaWeb 的 Servlet 中关于 doGet/doPost 方法调用;
  • Hibernate 中模板程序;
  • Spring 中 JDBCTemlate、HibernateTemplate 等。

interface

为什么要有接口:

  • 一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java 不支持多重继承。有了接口,就可以得到多重继承的效果。
  • 另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有 is-a 的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都

    支持USB连接。

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则必须能...”的思想。继承是一个 "是不是" 的关系,而接口实现则是 "能不能"

的关系。

接口(interface)是抽象方法常量值定义的集合。

接口的特点:

  • 用 interface 来定义,和 class 是并列关系;
  • 接口中的所有成员变量都默认是由 public static final 修饰的;
  • 接口中的所有抽象方法都默认是由 public abstract 修饰的;
  • 接口中没有构造器,意味着接口不能实例化;
  • 接口采用多继承机制,接口让类去实现(implements)的方式来使用:
    • 如果实现覆盖了接口的所有抽象方法,则此实现类就可以实例化;
    • 如果实现类没有覆盖接口中的所有抽象方法,则此实现类仍未一个抽象类。
  • 一个类可以实现多个接口。语法class SubClass extends SuperClass implements InterfaceA, InterfaceB {}
  • 接口也可以继承其它接口,并且可以多继承。语法interface AA extends BB, CC {}
  • 与继承关系类似,接口与实现类之间存在多态性

抽象类和接口的异同:

区别点 抽象类 接口
定义 包含抽象方法的类 主要是抽象方法和全局常量的集合
组成 构造器、抽象方法、普通方法、常量、变量 抽象方法、常量
使用 子类继承抽象类(extends) 子类实现接口(implements)
关系 抽象类可以实现多个接口 接口不能继承抽象类,但允许继承多个接口
常用设计模式 模版方法 简单工厂、工厂方法、代理
对象 都不能直接实例化,都必须通过对象的多态性产生实例化对象
局限 抽象类有单继承的局限 接口没有此局限
实际 作为一个模板 是作为一个标准或是表示一种能力
选择 如果抽象类和接口都可以使用的话,优先使用接口,因为避免单继承的局限
// 如何定义接口:JDK7及以前,只能定义全局常量和抽象方法。
// 全局常量:public static final,书写时可省略;
// 抽象方法:public abstract,书写时可省略; public interface Flyable {
public static final int MAX_SPEED = 7900;
init MIN_SPEED = 1; public abstract void fly();
void stop();
} class Plane implements Flyable {
public void fly() {
System.out.println("通过引擎起飞");
} punlic void stop() {
System.out.println("通过减速停止");
}
} // 如何定义接口:JDK8,除了能定义全局常量和抽象方法外,还能定义静态方法、默认方法。

Java8 接口新特性

JDK8,除了能定义全局常量和抽象方法外,还能定义静态方法、默认方法。

静态方法:使用 static 关键字修饰。只能通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像 Collection/Collections 或者 Path/Paths 这样成对的接口和类。

默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java 8 API 中对 Collection、List、Comparator 等接口提供了丰富的默认方法。

public interface AA {
double PI = 3.14; public default void method() {
System.out.println("北京");
} default String method1() {
return "上海";
} public static void method2() {
System.out.println(“hello lambda!");
}
}

若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同

参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接

口时,会出现接口冲突。解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突。

若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非

抽象方法,则不会出现冲突问题。因为此时遵守类优先原则。接口中具有相同名称和参数的默认方法会被忽略。

代理模式

代理模式是 Java 开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。

信用卡是银行账户的代理, 银行账户则是一大捆现金的代理。 它们都实现了同样的接口, 均可用于进行支付。 消费者会非常满意, 因为不必随身携带大量现金; 商店老板同样会十分高兴, 因为交易收入能以电子化的方式进入商店的银行账户中, 无需担心存款时出现现金丢失或被抢劫的情况。

package com.parzulpan.java.ch04;

/**
* @Author : parzulpan
* @Time : 2020-11-22
* @Desc : 接口的应用:代理模式
*/ public class ProxyTest { public static void main(String[] args) {
Star s = new Proxy(new RealStar());
s.confer();
s.signContract();
s.bookTicket();
s.sing();
s.collectMoney();
}
} interface Star {
void confer();// 面谈 void signContract();// 签合同 void bookTicket();// 订票 void sing();// 唱歌 void collectMoney();// 收钱
} // 代理类
class RealStar implements Star { public void confer() {
} public void signContract() {
} public void bookTicket() {
} public void sing() {
System.out.println("明星:歌唱~~~");
} public void collectMoney() {
}
} // 被代理类
class Proxy implements Star {
private Star real; public Proxy(Star real) {
this.real = real;
} public void confer() {
System.out.println("经纪人面谈");
} public void signContract() {
System.out.println("经纪人签合同");
} public void bookTicket() {
System.out.println("经纪人订票");
} public void sing() {
real.sing();
} public void collectMoney() {
System.out.println("经纪人收钱");
}
}

应用场景:

  • 安全代理:屏蔽对真实角色的直接访问;
  • 远程代理:通过代理类处理远程方法调用(RMI);
  • 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象。

代理分类:

  • 静态代理(静态定义代理类);
  • 动态代理(动态生成代理类)。

内部类

当一个事物的内部,还有一个部分需要一个完整的结构进行描述。而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。

在 Java 中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。

内部类的分类:

  • 成员内部类(static 成员内部类 和 非 static 成员内部类);
  • 局部内部类(不谈修饰符,方法内、代码块内、构造器内)、匿名内部类。

成员内部类作为类的成员

  • 可以调用外部类的结构;
  • 可以被 static 修饰;
  • 可以被 4 中权限修饰符修饰。

成员内部类作为类

  • 可以在内部定义属性、方法、构造器等结构;
  • 可以声明为 abstract 的 ,因此可以被其它的内部类继承;
  • 可以声明为 final 的,即不能被其它类继承;
  • 编译以后生成 OuterClass$InnerClass.class 字节码文件。
class Person {
// 静态成员内部类
static class A { } // 非静态成员内部类
class B { } Person() {
class E { }
} public void method() {
class C { }
} {
class D { }
}
}

注意

练习和总结


以下代码的运行情况?

interface A {
int x = 0;
}
class B {
int x = 1;
}
class C extends B implements A {
public void pX() {
// System.out.println(x);
System.out.println(super.x); // 更改 1
System.out.println(A.x); // 更改 0
}
public static void main(String[] args) {
new C().pX();
}
}

编译出错,x 属性不明确。


以下代码的运行情况?

interface Playable {
void play();
} interface Bounceable {
void play();
} interface Rollable extends Playable, Bounceable {
Ball ball = new Ball("PingPang");
} class Ball implements Rollable {
private String name;
public String getName() {
return name;
}
public Ball(String name) {
this.name = name;
}
public void play() {
ball = new Ball("Football");
System.out.println(ball.getName());
}

interface Rollable 里的 ball 是 全局常量 public static final,Ball 中的 play() 重新给 ball 赋值了。


【Java基础】面向对象下的更多相关文章

  1. Java基础-面向对象第三大特性之多态(polymorphism )

    Java基础-面向对象第三大特性之多态(polymorphism) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.多态概述 多态是继封装,继承之后,面向对象的第三大特性,多态的 ...

  2. Java基础-面向对象第二特征之继承(Inheritance)

    Java基础-面向对象第二特征之继承(Inheritance) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.继承的概述 在现实生活中,继承一般指的是子女继承父辈的财产.在程序 ...

  3. java基础面向对象之类与对象

    java基础面向对象之类与对象 2017-01-14 1.面向对象的基本概念 以一种组建化的形式进行代码设计 1)在面向对象程序设计中包含有如下几种特性 •封装性:保护内部结构的安全性 •继承性:在已 ...

  4. Java基础(下)(JVM、API)

    Java基础(下) 第三部分:Java源程序的编辑 我们知道,计算机是不能直接理解源代码中的高级语言,只能直接理解机器语言,所以必须要把高级语言翻译成机器语言,计算机才能执行高级语言编写的程序. 翻译 ...

  5. 第二十七节:Java基础面向对象-静态,单例模式,继承详情知识点

    前言 Java基础面向对象-静态,单例模式,继承详情知识点.静态-static关键字,static变量,静态代码块,代码块(不加静态),对象创建过程,单例模式,继承. 静态-static关键字 // ...

  6. Java基础-面向对象第一特性之封装(Encapsulation)

    Java基础-面向对象第一特性之封装(Encapsulation) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.理解什么是面向过程和面向对象 面向过程与面向对象都是我们编程中 ...

  7. JAVA(一)JAVA基础/面向对象基础/高级面向对象

    成鹏致远 | lcw.cnblog.com |2014-01-23 JAVA基础 1.开发环境搭建 JAVA程序的执行流程 JAVA命令->要使用一个*.class文件(类文件)->通过c ...

  8. Java基础——面向对象

    Hello 大家好,我又来啦,今天我们来说说Java的面向对象. 还记得之前去面试几家公司的实习生职位,大部分面试官都问过我有关面向对象 的问题,不知道以后还会不会问,估计是不会了吧...(:3[▓▓ ...

  9. Java基础 (下)

    泛型 Java 泛型了解么?什么是类型擦除?介绍一下常用的通配符? Java 泛型(generics) 是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时 ...

  10. Java编程基础-面向对象(下)

    一.抽象类 1.引入:当定义一个类时,常常需要定义一些方法来描述该类的行为特征,但有时这些方法的实现方式是无法确定的.Java允许在定义方法时不写方法体,不包含方法体的方法为抽象方法,抽象方法必须使用 ...

随机推荐

  1. 使用Promise实现红绿灯交替重复亮

    红灯3秒亮一次,黄灯2秒亮一次,绿灯1秒亮一次:如何让三个灯不断交替重复亮灯?(用Promise实现) function red() { console.log('red'); } function ...

  2. 获取浏览器URL中查询字符串中的参数

    //http://www.runoob.com/index.html?name=xiaoming&age=23function showWindowHref(){ var sHref = wi ...

  3. 一种使用 Redis 深度驱动的,为构建轻量级分布式应用程序(Microservices)的工程方案

    Hydra 是一个轻量级的 NodeJS 库,用于构建分布式计算应用程序,比如微服务.我们对轻量级的定义是:轻处理外部复杂性和基础设施依赖 -- 而不是有限的轻处理. Hydra 声称对基础设施的依赖 ...

  4. 题解 洛谷 P5279 【[ZJOI2019]麻将】

    这题非常的神啊...蒟蒻来写一篇题解. Solution 首先考虑如何判定一副牌是否是 "胡" 的. 不要想着统计个几个值 \(O(1)\) 算,可以考虑复杂度大一点的. 首先先把 ...

  5. 升级openssl和openssh版本

    一.安装telnet-server服务(建议安装) 1. 查看系统是否已安装telnet-server,linux系统上默认已经安装telnet-client(或telnet),而telnet-ser ...

  6. java多线程之消费生产模型-使用synchronized解决虚假唤醒

    package com.wenshao.juc; /** * 生产者和消费者案例 * * @author Administrator * */ public class TestProductorAn ...

  7. springboot:异步调用@Async

    在后端开发中经常遇到一些耗时或者第三方系统调用的情况,我们知道Java程序一般的执行流程是顺序执行(不考虑多线程并发的情况),但是顺序执行的效率肯定是无法达到我们的预期的,这时就期望可以并行执行,常规 ...

  8. Object not found! The requested URL was not found on this server.... 报错解决方案

    服务器(centos6.5) lnmp 报错如下 Object not found! The requested URL was not found on this server. The link ...

  9. Day1 数据类型

    整数 十六进制和八进制使用0作为前缀,如 0x12f , 010浮点数 可以用科学计数法来表示很大或者很小的浮点数,如 1.23x10^9 可以写作 1.23e9 或者12.3e8 ,0.000012 ...

  10. Redis达到最大占用内存后的淘汰策略

    1. 查询Redis最大占用内存 # 查询最大占用内存 config get maxmemory # 为0时在64操作系统中不限制内存,在32位操作系统中最大为3GB 2. Redis设置最大占用内存 ...