面向对象下

这一章主要涉及其他关键字,包括 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. 20201213-1 HTML基本标签(一)

    > HTML 基本结构 <> </> 标签对   > 一个 HTML 文档由 4 个基本部分组成: 文档声明:<!DOCTYPE HTML>声明这是一个 ...

  2. 跨站点脚本编制 - SpringBoot配置XSS过滤器(基于Jsoup)

    1. 跨站点脚本编制   风险:可能会窃取或操纵客户会话和 cookie,它们可能用于模仿合法用户,从而使黑客能够以该用户身份查看或变更用户记录以及执行事务.   原因:未对用户输入正确执行危险字符清 ...

  3. IOS实现自动定位和手动选择城市功能

    IOS自动定位使用的是高德地图SDK 在高德开放平台http://lbs.amap.com/api/ios-sdk/down/ 下载2D地图SDK和搜索SDK 将SDK导入工程内 按照高德的配置说明进 ...

  4. 01-flask-helloWorld

    代码 from flask import Flask # 创建Flask对象 app = Flask(__name__) # 定义路由 @app.route('/') def index(): # 函 ...

  5. 【PY从0到1】第二节 字符串和数字

    #本课程都以代码形式呈现.现在进入Python的基础内容的学习. #由于本课程是从0开始分享,所以Python的基础内容是必不可少的.这也是Python量化的必经之路. #下面进入正题. #一般用'# ...

  6. 移动端SCSS

    一.什么是SASS SASS是一种强化CSS的辅助工具,提供了许多便利的写法,大大节省了设计者的时间,使得CSS的开发,变得简单可维护. #二.安装和使用(VS Code中) #1.安装 下载扩展文件 ...

  7. 谷歌学术: but your computer or network may be sending automated queries. To protect our users, we can't process your request right now. See Google Help for more information.

    原因是屏蔽了日本和新加坡的服务器,切换服务器为其他地方即可

  8. 记一次 oracle 数据库在宕机后的恢复

    系统:redhat 6.6 oracle版本: Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production 问题描述: ...

  9. 带你学习Python-基础知识的框架梳理

    对于编程初学者来说,在刚刚开始学习Python语言的时候,需要学习的内容有不少,比如计算机原理.网络.Web前端.后端.架构. 数据库都是必须掌握的知识点.因此初学者常常会陷入"只见树木,不 ...

  10. 一种简单的吉布斯采样modify中应用

    这是主函数clc; clear all; close all; %% 生成初始序列 sequenceOfLength = 20; sequenceOfPop = 4; sequence = produ ...