《码出高效》之面向对象基础

简介: 《码出高效》之面向对象基础

面向对象基础


面向对象四大特性


  1. 抽象
  2. 封装
  3. 继承
  4. 多态


OOP 理念

  1. 我是谁? getClass() 说明本质上是谁,而 toString() 是当前值为的名片;
  2. 我从哪里来? Object() 构造方法是生产对象的基本步骤,clone() 是繁殖对象的另一种方式;
  3. 我到哪里去? finalize() 是在对象销毁时触发的方法;
  4. 世界是否因你而不同? hashCode() 和 equals() 就是判断与其他元素是否相等的一组方法;
  5. 与他人如何协调? wait() 和 notify() 是对象间通信与写作的一组方法;


克隆

克隆分为浅拷贝、一般深拷贝和彻底深拷贝。


浅拷贝只复制当前对象的所有基本数据类型,以及相应的引用变量;


彻底深拷贝是在成功 clone 一个对象之后,此对象与母对象在任何引用路径上都不存在共享的实例对象,但是引用路径递归越深,则越接近 JVM 底层对象,且发现彻底深拷贝实现难度越大。


归根结底,慎用 Object 的 clone() 方法来拷贝对象,因为对象的 clone() 方法默认是浅拷贝,若想实现深拷贝,则需要复写 clone() 方法实现引用对象的深度遍历式拷贝。


类的定义

类的定义有访问级别、类型、类名、是否抽象、是否静态、是否继承、是否实现。


  1. 访问级别:public 和 无访问控制符
  2. 类型:class、interface、enum


接口与抽象类

image.png

内部类

内部类本身就是一个属性,与其他属性定义方式一致


  1. 静态内部类:如:static class StaticInnerClass{}
  2. 成员内部类:如:private class InstanceInnerClass{}
  3. 局部内部类:定义在方法或者表达式内部
  4. 匿名内部类:如:(new Thread(){}).start()


访问权限控制

image.png


注意:为什么子类中不能访问另一个包中父类中的protected方法?


其实这个问题问法是错的,在子类中是可以访问另一个包中父类中的 protected 方法,能问出这样的问题,多半是在不同包的子类中创建了父类对象,通过父类对象去调用 protected 方法,结果发现编译不通过,所以才会萌生这样的疑问。


正确的访问方式:


1. super.protectedMethod()

2. 创建子类对象 instance,通过 instance.protectedMethod() 访问


在定义类时,推荐访问控制级别从严处理.


  1. 如果不允许外部直接通过 new 创建对象, 构造方法必须是 private ;
  2. 工具类不允许有 public 或 default 构造方法;
  3. 类非 static 成员变雪并且与子类共享, 必须是 protected ;
  4. 类非 static 成员变量并且仅在本类使用, 必须是 private ;
  5. 类 static 成员变量如果仅在本类使用, 必须是private ;
  6. 若是 static 成员变量, 必须考虑是否为 fianl ;
  7. 类成员方法只供类内部调用, 必须是 private ;
  8. 类成员方法只对继承类公开, 那么限制为 protected 。


类关系

  1. 继承: extends (is-a)
  2. 实现: implements (can-do)
  3. 组合: 类是成员变量 (contains-a) ,比如头和身体的关系
  4. 聚合: 类是成员变量 (has-a),比如小狗和狗绳的关系,狗绳完全可以复用在一条小狗身上
  5. 依赖: import 类 (use-a)


序列化

  1. Java 原生序列化:实现 Serializable 接口实现该类对象的序列化。基于性能及兼容性考虑,不推荐使用
  2. Hessian 序列化:Hessian 序列化是一种支持动态类型、跨语言、基于对象传输的网络协议。特点:①自描述序列化类型;②与语言无关;③写简单,比 Java 原生序列化高效。Hessian 会把复杂对象所有属性存储在一个 Map 中进行序列化。所以在父类、子类存在同名成员变量的情况下, Hessian 序列化时,先序列化子类,然后序列化父类,因此反序列化结果会导致子类同名成员变量被父类的值覆盖。
  3. Json 序列化:一种轻量级的数据交换格式,在序列化过程抛弃了类信息,反序列化时只有提供类型信息才能准确的反序列化。JSON 可读性好,方便调试。


序列化常常成为黑客的攻击点,如何防范黑客攻击?有些对象的敏感属性不需要进行序列化传输,可以加 transient 关键字,避免把此属性信息转换成序列化二进制流。如果一定要传递对象的敏感信息,可以使用对称与非对称加密方式独立传输,在使用某个方法把属性还原到对象中。


方法


方法签名

方法签名包括方法名称和参数列表,是 JVM 标识方法的唯一索引,不包括返回值,更不包括访问权限控制符、异常类型等。


参数

package top.simba1949;
/**
 * @author SIMBA1949
 * @date 2019/8/15 8:10
 */
public class ParamTest {
    private static int intStatic = 222;
    private static String stringStatic = "old string";
    private static StringBuilder stringBuilderStatic = new StringBuilder("old stringBuilder");
    public static void main(String[] args) {
        /**
         * 参数是局部变量、拷贝静态变量的 777,并存入虚拟机栈的局部变量表中。
         * 虽然方法内部的 intStatic 与静态变量同名,但是因为作用域就近原则,它是局部变量的参数,所有的操作与静态变量无关
         * 
         * 无参方法先把本地赋值的 888 压入到虚拟机栈中的操作栈,然后给静态变量 intStatic 赋值
         */
        // 输出依然是 222
        method(intStatic);
        System.out.println(intStatic);
        // 无参方法调用之后,反而修改为 888
        method();
        System.out.println(intStatic);
        // String 是 immutable 对象,String 没有提供任何方法用于修改对象
        // 输出依然是 old string
        method(stringStatic);
        System.out.println(stringStatic);
        // 实参向形参传递的是引用地址,形参修改引用地址对象属性,实参获取该引用地址对象的属性也是变化后的
        // 输出 old stringBuilder.method.first-method.second-new method's method
        method(stringBuilderStatic, stringBuilderStatic);
        System.out.println(stringBuilderStatic);
    }
    public static void method(int intStatic){
        intStatic = 777;
    }
    public static void method(){
        intStatic = 888;
    }
    public static void method(String stringStatic){
        stringStatic = "new string";
    }
    public static void method(StringBuilder stringBuilderStatic1, StringBuilder stringBuilderStatic2){
        stringBuilderStatic1.append(".method.first-");
        stringBuilderStatic2.append("method.second-");
        // 引用重新赋值
        stringBuilderStatic1 = new StringBuilder("new stringBuilder");
        stringBuilderStatic2.append("new method's method");
    }
}


方法重载

  1. 精确匹配
  2. 如果是基本数据类型,自动转换成更大表示范围的基本类型
  3. 通过自动拆箱与装箱
  4. 通过子类向上转型集成路线依次匹配
  5. 通过可变参数匹配


例外

package top.simba1949;
/**
 * @author SIMBA1949
 * @date 2019/8/14 7:32
 */
public class OverloadTest {
  public static void main(String[] args) {
  // 以下重载都会直接报错,如果对应形参列表则不会出现报错
//  overloadMethod(7, 7);
//  longOverload(1L, 1L);
//  doubleOverload(1.0, 1.0);
//  floatOverload(1.0f,1.0f);
  // 如果对应形参列表则不会出现报错,即精确匹配
  int a = 1; Integer b = 2;
  overloadMethod(a, b);
  overloadMethod(b, a);
  }
  public static void overloadMethod(int a, Integer b){
  System.out.println("int Integer");
  }
  public static void overloadMethod(Integer a, int b){
  System.out.println("Integer int");
  }
  public static void longOverload(Long a, long b){}
  public static void longOverload(long a, Long b){}
  public static void doubleOverload(Double a, double b){}
  public static void doubleOverload(double a, Double b){}
  public static void floatOverload(Float a, float b){}
  public static void floatOverload(float a, Float b){}
}


泛型

泛型的本质是类型参数化,解决不确定具体对象类型的问题。


E 代表 Element ,用于集合中的元素;T 代表 the Type of object ,表示某个类;K 代表 Key,V 代表 Value,用于键值对元素。


泛型可以定义在类、接口、方法中,编译器通过识别尖括号和尖括号内的字母来解析泛型。


  1. 尖括号里的每一个元素都指代一种未知类型。
  2. 尖括号未知非常讲究,必须在类名之后或者方法返回值之前。
  3. 泛型在定义处只具备执行 Object 方法的能力。
  4. 对于编译之后的字节码指令,其实没有这些花头花脑的方法签名,充分说明了泛型知识一种编写代码时的语法检查。


基本数据类型

image.png


引用分为两种数据类型:引用变量本身 refvar (Reference Variable)和引用指向的对象 refobj (Referred Object)。


refvar 是基本的数据类型,它的默认值为 null,存储 refobj 的首地址,可以直接使用 == 进行等值判断。refvar.hashCode() 返回的值, 只是对象的某种哈希计算,可能与地址有关,与 refvar 本身存储的内存单元地址是两回事。refvar 均占用 4B 空间。一个 refvar 至多存储一个 refobj 的首地址。


无论 refobj 是多么小的对象,最小占用的存储空间是 12B (用于存储基本信息,成为对象头),但是由于存储空间分配必须是 8B 的倍数,所有初始分配的空间最少是 16B。


image.png


  1. 对象头( Object Header ):对象头占用 12 个字节,存储内容包括对象标记 (markOop) 和类元信息 (klassOop)。对象标记存储对象本身运行时的数据,如哈希码、GC 标记、锁信息、线程关联信息等,这部分数据在 64 位 JVM 上占用8 个字节,称为 “Mark Word”。为了存储更多的状态信息,对象标记的存储格式是非固定的(具体与 JVM 的实现有关)。类元信息存储的是对象指向它的类元数据(即Klass )的首地址,占用4 个字节,与 refvar 开销一致。
  2. 实例数据( Instance Data ):存储本类对象的实例成员变量和所有可见的父类成员变量。
  3. 对齐填充( Padding ):对象的存储空间分配单位是 8 个字节,如果一个占用大小为16 个字节的对象,增加一个成员变量 byte 类型,此时需要占用17 个字节,但是也会分配24 个字节进行对齐填充操作。


合理利用包装类的缓存

基本数据类型的包装类,推荐使用 valueOf(),合理利用缓存,提升程序性能。


各个包装类的缓存区间如下


  • Boolean :使用静态 final 变量定义, valueOf() 就是返回这两个静态值。
  • Byte: 表示范围是 -128~127 ,全部缓存。
  • Short:表示范围是 -32768~32767 ,缓存范围是 -128~127 。
  • Character: 表示范围是 0~65535 ,缓存范围是 0~127 。
  • Long :表示范围是[-2 63 2^{63}2
package top.simba1949;
/**
 * @author SIMBA1949
 * @date 2019/8/14 7:32
 */
public class OverloadTest {
  public static void main(String[] args) {
  Long a1 = 127L;
  Long a2 = 127L;
  Long b1 = 128L;
  Long b2 = 128L;
  System.out.println("Long max cached value is 127: a1 == a2 result is true > " + (a1 == a2));
  System.out.println("Long max cached value is 127: b1 == b2 result is false > " + (b1 == b2));
    }
}


目录
相关文章
ly~
|
2月前
|
存储 算法 编译器
游戏开发中,C 语言的性能优势体现在哪些方面?
在游戏开发中,C 语言凭借其对硬件的直接访问和内存操作的精准控制,能够显著提升性能。它允许开发者手动管理内存,优化数据存储和读取,充分利用显卡等硬件资源,实现流畅的图形渲染和音效处理。作为一种接近底层的语言,C 语言具有高效的执行速度,适用于物理引擎和碰撞检测等高性能需求模块,并且提供了丰富的运算符和数据类型,便于实现高效的算法。此外,C 语言代码具有良好的可移植性和跨平台性,支持多种操作系统和硬件平台,减少了多平台发布的开发成本。编译器提供的优化选项和手动代码优化的灵活性进一步提升了游戏的整体性能。
ly~
112 5
|
7月前
|
设计模式 算法 程序员
代码之美:追求简洁高效的编程艺术
【2月更文挑战第16天】 在数字世界的构建中,编程不仅仅是一门科学,更是一种艺术。本文将探讨如何在编程实践中追求简洁与效率的完美结合,揭示编程中的美学原则和实用技巧。通过对设计模式、代码重构以及性能优化等关键技术概念的深入分析,我们旨在为开发者提供一套提升代码质量、实现技术突破的思维工具。
|
7月前
|
设计模式 缓存 算法
编码之道:从简洁到高效的技术感悟
【2月更文挑战第29天】在软件开发的世界中,编码不仅仅是一门科学,更是一门艺术。本文探讨了从简洁性到高效性的编码实践,揭示了如何通过持续的学习和实践来提升代码质量。我们将深入分析重构的技巧、设计模式的应用以及性能优化的策略,旨在为追求卓越的开发者提供实用的指导和灵感。
|
7月前
|
前端开发 JavaScript Java
面向对象编程的艺术:构建高效可扩展的软件
面向对象编程的艺术:构建高效可扩展的软件
面向对象编程的艺术:构建高效可扩展的软件
|
2月前
|
PHP 开发者
PHP编程中的面向对象基础
【9月更文挑战第36天】在PHP的世界中,面向对象编程(OOP)是一块基石。它不仅为代码带来了结构、可维护性与重用性,还让复杂的问题变得简单化。通过掌握类与对象、继承与多态等核心概念,开发者可以构建出更加强大和灵活的应用。本文将引导你理解这些概念,并通过实例展示如何在PHP中应用它们,让你轻松驾驭OOP的力量。
|
3月前
|
PHP 开发者
PHP编程中的面向对象基础与实践
【9月更文挑战第27天】在PHP的海洋里,面向对象编程(OOP)是一艘强大的船,它不仅能让代码组织得更加优雅,还能提高开发效率。本文将带你领略OOP的魅力,从基础概念到实际应用,让你轻松驾驭这艘船,开启高效编程之旅。
|
5月前
|
SQL Rust 算法
开发与运维编程问题之常见的编程范式的声明式编程如何解决
开发与运维编程问题之常见的编程范式的声明式编程如何解决
|
6月前
|
Java 关系型数据库 开发者
Java编程设计原则:构建稳健、可维护的软件基石
Java编程设计原则:构建稳健、可维护的软件基石
|
6月前
|
设计模式 算法 数据库
现代软件开发中的设计模式与效率优化
在当今快节奏的软件开发环境中,设计模式不仅仅是代码组织的工具,更是提升开发效率和代码质量的重要利器。本文探讨了几种常用的设计模式在实际项目中的应用与优化策略,旨在帮助开发者在面对复杂系统和变化需求时,能够更加高效地进行软件开发。
61 1
|
7月前
|
算法 程序员 C语言
C++设计哲学:构建高效和灵活代码的艺术
C++设计哲学:构建高效和灵活代码的艺术
139 1
下一篇
DataWorks