关于本人对OOP编程一些知识记录

530 阅读12分钟

一:怎么理解OOP编程思想

简单理解:

  1. --->  人
    
  2.      成员 ---> 人的属性比如:眼睛
    
  3.      方法 ---> 人的属性行为,比如:眼睛会眨眼、流泪
    

二:基础知识

句柄操纵对象

String s; //这个创建的只是s句柄,并不是对象
String s1 = new String("abc");//这个创建了一个s1句柄,并让句柄指向了new String("abc")这个对象

句柄数据存于:堆栈中。对象数据存储于:堆中。

数据保存在什么地方

程序运行时,我们应当对数据保存到什么地方的有个基本的了解。特别是内存的分配。有6个地方可以保存数据:

寄存器:这是最快的保存区域,因为它和其他的保存方式不同,它位于:处理器内部。 寄存器的数量十分有限,所以寄存器是根据需要有编译器分配的。我们没有直接的控制权,也不能在自己的程序里找到寄存器的任何踪迹。

堆栈:驻留于常规 RAM(随机访问存储器)区域,但可通过它的“堆栈指针”获得处理的直接支持。堆 栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存。这是一种特别快、特别有效的数据保存 方式,仅次于寄存器。创建程序时,Java 编译器必须准确地知道堆栈内保存的所有数据的“长度”以及“存 在时间”。这是由于它必须生成相应的代码,以便向上和向下移动指针。这一限制无疑影响了程序的灵活 性,所以尽管有些 Java 数据要保存在堆栈里——特别是对象句柄,但Java 对象并不放到其中。

堆:一种常规用途的内存池(也在 RAM 区域),其中保存了Java 对象。和堆栈不同,“内存堆”或 “堆”(Heap)最吸引人的地方在于编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要 在堆里停留多长的时间。因此,用堆保存数据时会得到更大的灵活性。要求创建一个对象时,只需用new 命 令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存。当然,为达到这种灵活性,必然 会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!

静态存储:这儿的“静态”(Static)是指“位于固定位置”(尽管也在 RAM 里)。程序运行期间,静 态存储的数据将随时等候调用。可用static 关键字指出一个对象的特定元素是静态的。但 Java 对象本身永 远都不会置入静态存储空间。

常熟存储:常数值通常直接置于程序代码内部。这样做是安全的,因为它们永远都不会改变。有的常数

需要严格地保护,所以可考虑将它们置入只读存储器(ROM)。

非RAM存储:若数据完全独立于一个程序之外,则程序不运行时仍可存在,并在程序的控制范围之外。 其中两个最主要的例子便是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,通常会发给 另一台机器。而对于固定对象,对象保存在磁盘中。即使程序中止运行,它们仍可保持自己的状态不变。对 于这些类型的数据存储,一个特别有用的技巧就是它们能存在于其他媒体中。一旦需要,甚至能将它们恢复 成普通的、基于RAM 的对象。

主要类型(基本数据类型)

int i = 0;
String str = new String("a");
不知道大家有没有想过,为什么基本数据类型不需要new就能创建对象?

有一系列类需特别对待;可将它们想象成“基本”、“主要”或者“主”(Primitive)类型,进行程序设计 时要频繁用到它们。之所以要特别对待,是由于用 new 创建对象(特别是小的、简单的变量)并不是非常有 效,因为new 将对象置于“堆”里。对于这些类型,Java 采纳了与 C 和 C++相同的方法。也就是说,不是用 new 创建变量,而是创建一个并非句柄的“自动”变量。这个变量容纳了具体的值,并置于堆栈中,能够更 高效地存取。 Java 决定了每种主要类型的大小。就象在大多数语言里那样,这些大小并不随着机器结构的变化而变化。这 种大小的不可更改正是 Java 程序具有很强移植能力的原因之一。

精度丢失问题

在Java中进行高精度运算时会存在进度丢失的问题,在int转float、long转float,long转double 这些运算中会出现精度丢失的问题,Java本身提供了两个类来解决这方面的问题,不过牺牲了速度,但换来了精度:

BigInteger 支持任意精度的整数。也就是说,我们可精确表示任意大小的整数值,同时在运算过程中不会丢 失任何信息。

BigDecimal 支持任意精度的定点数字。

初始化和清除

对于方法的创建,可将其想象成为自己写的每个类都调用一次 initialize()。这个名字提醒我们在使用对象 之前,应首先进行这样的调用。但不幸的是,这也意味着用户必须记住调用方法。在 Java 中,由于提供了名 为“构建器”的一种特殊方法,所以类的设计者可担保每个对象都会得到正确的初始化。

程序员都知道“初始化”的重要性,但通常忘记清除的重要性。毕竟,谁需要来清除一个int 呢?但是对于 库来说,用完后简单地“释放”一个对象并非总是安全的。当然,Java 可用垃圾收集器回收由不再使用的对 象占据的内存。现在考虑一种非常特殊且不多见的情况。假定我们的对象分配了一个“特殊”内存区域,没 有使用 new。垃圾收集器只知道释放那些由new 分配的内存,所以不知道如何释放对象的“特殊”内存。为 解决这个问题,Java 提供了一个名为finalize()的方法,可为我们的类定义它。在理想情况下,它的工作原 理应该是这样的:一旦垃圾收集器准备好释放对象占用的存储空间,它首先调用 finalize(),而且只有在下 一次垃圾收集过程中,才会真正回收对象的内存。所以如果使用finalize(),就可以在垃圾收集期间进行一 些重要的清除或清扫工作。 垃圾收集只跟内存有关!

RTTI(运行期鉴定)和反射

RTTI 和“反射”之间唯一的区别就是对 RTTI 来 说,编译器会在编译期打开和检查.class 文件。换句话说,我们可以用“普通”方式调用一个对象的所有方 法;但对“反射”来说,.class 文件在编译期间是不可使用的,而是由运行期环境打开和检查。

//简单的代码
Student student = new Student();
User user = (User) student;// 这个造型上溯即为RTTI(运行期鉴定)

//反射 -- 去获取一个不在本程序内的相关信息,在编译期间是不会被RTTI检测的
 Class c = Class.forName("xxx/xx.java");
 Method[] m = c.getMethods();
 Constructor[] ctor = c.getConstructors();

传递和返回对象

Java 中的所有自变量或参数传递都是通过传递句柄进行的。也就是说,当我们传递“一个对 象”时,实际传递的只是指向位于方法外部的那个对象的“一个句柄”。所以一旦要对那个句柄进行任何修 改,便相当于修改外部对象。此外:

  • 参数传递过程中会自动产生别名问题
  • 不存在本地对象,只有本地句柄
  • 句柄有自己的作用域,而对象没有
  • 对象的“存在时间”在Java 里不是个问题
  • 没有语言上的支持(如常量)可防止对象被修改(以避免别名的副作用)

克隆对象

若需修改一个对象,同时不想改变调用者的对象,就要制作该对象的一个本地副本。这也是本地副本最常见 的一种用途。若决定制作一个本地副本,只需简单地使用 clone()方法即可。 如果希望一个类能够克隆,那么:

  1. 实现Cloneable 接口
  2. 覆盖clone()
  3. 在自己的clone()中调用 super.clone()
  4. 在自己的clone()中捕获违例
class Int2 implements Cloneable {
    private int i;
    public Object clone() {
        Object o = null;
        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("Int2 can't clone");
        }
        return o;
    }
}

上面Demo是指一个简单的克隆,如果是把上面的类存在在一个集合中,然后把整个集合做克隆就需要做深度拷贝了。如下:

    Vector v = new Vector();
    for (int i = 0; i < 10; i++) {
        v.addElement(new Int2(i));
    }
    Vector v2 = (Vector) v.clone(); //1
    for (int i = 0; i < v.size(); i++) {
        v2.setElementAt(((Int2) v2.elementAt(i)).clone(), i);//2
    }
    //如果没有做第二步的for遍历clone(),那么v2里面存储的数据句柄指向还是和v里面的数据句柄指向的是相同的,所以不管是修改v2还是v它们的数据都会变动

当然有克隆存在一定的安全风险,所以我们也可以消除克隆(关闭克隆功能) 如果希望一个类关闭克隆,那么:

  1. 将类设置为final的即可,且不实现Cloneable接口

只读类

public class Immutable1 {
 private int data;
 public Immutable1(int initVal) {
 data = initVal;
 }
 public int read() { return data; }
 public boolean nonzero() { return data != 0; }
 public Immutable1 quadruple() {
 return new Immutable1(data * 4);
 }
 static void f(Immutable1 i1) {
 Immutable1 quad = i1.quadruple();
 System.out.println("i1 = " + i1.read());
 System.out.println("quad = " + quad.read());
 }
 public static void main(String[] args) {
 Immutable1 x = new Immutable1(47);
 System.out.println("x = " + x.read());
 f(x);
 System.out.println("x = " + x.read());
 }
} 
//所有数据都设为private,可以看到没有任何 public 方法对数据作出修改。事实上,确实需要修改一个对象
的方法是quadruple(),但它的作用是新建一个Immutable1 对象,初始对象则是原封未动的。
方法 f()需要取得一个 Immutable1 对象,并对其采取不同的操作,而 main()的输出显示出没有对x 作任何修
改。因此,x 对象可别名处理许多次,不会造成任何伤害,因为根据 Immutable1 类的设计,它能保证对象不
被改动。

对象序列化 作用:

  1. 接口的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子。这一过程亦可通过网络进行,这意味着序列化机制能自动补偿操作系统间的差异。换句话说,可以先在Windows 机器上创建一个对象,对其序列化,然后通过网络发给一台 Unix 机器,然后在那里准确无误地重新“装配”。不必关心数据在不同机器上如何表示,也不必关心字节的顺序或者其他任何细节。

  2. 可以实现“有限的持久化”,“持久化”意味着对象的“生存时间”并不取决于程序是否正在执行——它存在或“生存”于程序的每一次调用之间。通过序列化一个对象,将其写入磁盘,以后在程序重新调用时重新恢复那个对象,就能圆满实现一种“持久”效果。之所以称其为“有限”,是因为不能用某种“persistent”(持久)关键字简单地地定义一个对象,并让系统自动照看其他所有细节问题(尽管将来可能成为现实)。相反,必须在自己的程序中明确地序列化和组装对象。

  3. 序列化也对:RMI(远程方法调用)提供了支持,同时也可用于克隆技术(不过用序列化技术用于克隆没有clone()方法有效,且不稳定)

远程调用RMI

我相信现在公司内部服务之间的调用基本都是用的“远程调用技术”,比如:Dubbo,Feign等相关技术,但是它们有个共同的特点就是:“以接口的形式暴露”。那么为什么会这样去实现呢,这就涉及到远程调用的概念了:

远程接口概念:

RMI 对接口有着强烈的依赖。在需要创建一个远程对象的时候,我们通过传递一个接口来隐藏基层的实施细 节。所以客户得到远程对象的一个句柄时,它们真正得到的是接口句柄。这个句柄正好同一些本地的根代码 连接,由后者负责通过网络通信。但我们并不关心这些事情,只需通过自己的接口句柄发送消息即可。 创建一个远程接口时,必须遵守下列规则:

(1) 远程接口必须为public 属性(不能有“包访问”;也就是说,它不能是“友好的”)。否则,一旦客户 试图装载一个实现了远程接口的远程对象,就会得到一个错误。

(2) 远程接口必须扩展接口 java.rmi.Remote。

(3) 除与应用程序本身有关的违例之外,远程接口中的每个方法都必须在自己的throws 从句中声明 java.rmi.RemoteException。

(4) 作为参数或返回值传递的一个远程对象(不管是直接的,还是在本地对象中嵌入)必须声明为远程接 口,不可声明为实施类。

当然还有很重要的一点:就是“远程调用接口”方法的中的入参对象、返回参数对象,必须要有默认的构造函数的,因为客户得到的只是指向接口的一个句柄,而非实现它的那个类。 必须为远程对象明确定义构建器,即使只准备定义一个默认构建器,用它调用基础类构建器。必须把它明确 地编写出来

-----当然关于OOP的相关知识肯定不止这些肯定还有很多比如:抽象、接口、继承、IO、网络编程等等。不过本人就先记录到这了-----