精华内容
下载资源
问答
  • 本文通过对象的创建步骤中的检查加载->分配内存->内存空间初始化->设置->对象初始化,对象的内存布局,什么是垃圾的两种算法以及四种引用,讲述JVM中对象及引用。

    本文通过对象的创建步骤中的检查加载->分配内存->内存空间初始化->设置->对象初始化,对象的内存布局,什么是垃圾的两种算法以及四种引用,讲述JVM中对象及引用,本篇篇幅较长,适合点赞+收藏。有什么错误希望大家直接指出~

    对象的创建

    当JVM加载后遇到一条new指令首先检查是否被类加载器加载,如果没有,那必须先执行相应的类加载过程。类加载就是把 class 加载到 JVM 的运行时数据区的过程(类加载后面有专门的专题讲)。

    一、检查加载

    首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用( 符号引用:符号引用以一组符号来描述所引用的目标),并且检查类是否已经被加载。
    解析和初始化过。

    二、分配内存

    接下来虚拟机将为新生对象分配内存。为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。
    指针碰撞
    如果 Java 堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离,这种分配方式称为“ 指针碰撞”。


    空闲列表
    如果 Java 堆中的内存并不是规整的,已使用的内存和空闲的内存相互交错,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“ 空闲列表”。

    选择哪种分配方式由 Java 堆是否规整决定,而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。(这部分知识先了解,后续结合垃圾回收器一起去理解)
    如果是 Serial、ParNew 等带有压缩的整理的垃圾回收器的话,系统采用的是指针碰撞,既简单又高效。
    如果是使用 CMS 这种不带压缩(整理)的垃圾回收器的话,理论上只能采用较复杂的空闲列表。

    并发安全
    除如何划分可用空间之外,还有另外一个需要考虑的问题是对象创建在虚拟机中是非常频繁的行为,即使是仅仅修改一个指针所指向的位置,在并发情况下也并不是线程安全的,可能出现正在给对象 A 分配内存,指针还没来得及修改,对象 B 又同时使用了原来的指针来分配内存的情况。
    CAS机制
    解决这个问题有两种方案,一种是对分配内存空间的动作进行同步处理——实际上虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性;
    分配缓冲
    另一种是把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在 Java 堆中预先分配一小块私有内存,也就是本地线程分配缓冲(Thread Local Allocation Buffer,TLAB),JVM 在线程初始化时,同时也会申请一块指定大小的内存,只给当前线程使用,这样每个线程都单独拥有一个 Buffer,如果需要分配内存,就在自己的 Buffer 上分配,这样就不存在竞争的情况,可以大大提升分配效率,当 Buffer 容量不够的时候,再重新从 Eden 区域申请一块继续使用。
    TLAB 的目的是在为新对象分配内存空间时,让每个 Java 应用线程能在使用自己专属的分配指针来分配空间,减少同步开销。
    TLAB 只是让每个线程有私有的分配指针,但底下存对象的内存空间还是给所有线程访问的,只是其它线程无法在这个区域分配而已。当一个 TLAB 用满(分配指针 top 撞上分配极限 end 了),就新申请一个 TLAB。
    参数:
    -XX:+UseTLAB:允许在年轻代空间中使用线程本地分配块(TLAB)。默认情况下启用此选项。要禁用 TLAB,请指定-XX:-UseTLAB。(Enables the use of thread-local allocation blocks (TLABs) in the young generation space. This option is enabled by default. To disable the use of TLABs, specify -XX:-UseTLAB.)

    三、内存空间初始化

    (注意不是构造方法)内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(如 int 值为 0,boolean 值为 false 等等)。这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

    四、设置

    接下来,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息(Java classes 在 Java hotspot VM 内部表示为类元数据)、对象的哈希码、对象的 GC 分代年龄等信息。这些信息存放在对象的对象头之中。

    五、对象初始化

    在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚刚开始,所有的字段都还为零值。所以,一般来说,执行 new 指令之后会接着把对象按照程序员的意愿进行初始化(构造方法),这样一个真正可用的对象才算完全产生出来。

    对象的内存布局

    在 HotSpot 虚拟机中,对象在内存中存储的布局可以分为 3 块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

     

    对象头包括两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
    对象头的另外一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。如果对象是一个 Java 数组,那么在对象头中还有一块用于记录数组长度的数据。
    对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于 HotSpot VM 的自动内存管理系统要求对对象的大小必须是 8 字节的整数倍。当对象其他数据部分没有对齐时,就需要通过对齐填充来补全。

    对象的访问定位

    建立对象是为了使用对象,我们的 Java 程序需要通过栈上的 reference 数据来操作堆上的具体对象。目前主流的访问方式有使用句柄和直接指针两种。
    句柄
    如果使用句柄访问的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。
    使用句柄来访问的最大好处就是 reference 中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。


    直接指针
    如果使用直接指针访问, reference 中存储的直接就是对象地址。
    这两种对象访问方式各有优势,使用直接指针访问方式的最大好处就是速度更快,它节省了一次指针定位的时间开销,由于对象的访问在 Java 中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本。
    对 Sun HotSpot 而言,它是使用直接指针访问方式进行对象访问的。

    判断对象存活

    在堆里面存放着几乎所有的对象实例,垃圾回收器在进行回收前,要做的事情就是确定这些对象中哪些还是“存活”着,哪些已经“死去”(死去代表着不可能再被任何途径使用得对象了)

    什么是垃圾 ?

    C 语言申请内存:malloc free ,C++: new delete。 C/C++ 手动回收内存
    Java: new。Java 是自动内存回收,编程上简单,系统不容易出错。
    手动释放内存,容易出两种类型的问题:
    1、忘记回收
    2、多次回收
    没有任何引用指向的一个对象或者多个对象(循环引用)

    引用计数法

    在对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1,当引用失效时,计数器减 1。Python 在用,但主流虚拟机没有使用,因为存在对象相互引用的情况,这个时候需要引入额外的机制来处理,这样做影响效率,
    在代码中看到,只保留相互引用的对象还是被回收掉了,说明 JVM 中采用的不是引用计数法。

    可达性分析( 面试时重要的知识点,牢记)

    来判定对象是否存活的。这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots 没有任何引用链相连时,则证明此对象是不可用的。

    作为 GC Roots 的对象包括下面几种(重点是前面 4 种):

    1、虚拟机栈(栈帧中的本地变量表)中引用的对象;各个线程调用方法堆栈中使用到的参数、局部变量、临时变量等。
    2、方法区中类静态属性引用的对象;java 类的引用类型静态变量。
    3、方法区中常量引用的对象;比如:字符串常量池里的引用。
    4、本地方法栈中 JNI(即一般说的 Native 方法)引用的对象。
    5、JVM 的内部引用(class 对象、异常对象 NullPointException、OutofMemoryError,系统类加载器)。(非重点)
    6、所有被同步锁(synchronized 关键)持有的对象。(非重点)
    7、JVM 内部的 JMXBean、JVMTI 中注册的回调、本地代码缓存等(非重点)
    8、JVM 实现中的“临时性”对象,跨代引用的对象( 在使用分代模型回收只回收部分代的对象,这个后续会细讲,先大致了解概念)(非重点)

    以上的回收都是对象,类的回收条件:

    注意 Class  要被回收,条件比较苛刻,必须同时满足以下的条件(仅仅是可以,不代表必然,因为还有一些参数可以进行控制):
    1、该类所有的实例都已经被回收,也就是堆中不存在该类的任何实例。
    2、加载该类的 ClassLoader 已经被回收。
    3、该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
    4、参数控制:-Xnoclassgc:禁用类的垃圾收集(GC) 。这样可以节省一些GC时间, 从而缩短了应用程序运行期间的中断时间。
    当您-Xnoclasagc在启动时指定时,应用程序中的类对象在GC期间将保持不变,并且始终被认为是活动的。这可能会导致更多的内存被永久占用,如果不谨慎使用,将抛出内存不足异常。

    废弃的常量和静态变量的回收其实就和 Class 回收的条件差不多

    Finalize  方法

    即使通过可达性分析判断不可达的对象,也不是“非死不可”,它还会处于“缓刑”阶段,真正要宣告一个对象死亡,需要经过两次标记过程,一次是没有找到与 GCRoots 的引用链,它将被第一次标记。随后进行一次筛选(如果对象覆盖了 finalize),我们可以在 finalize 中去拯救。
    代码演示:

    /**
     * @author macfmc
     * @date 2020/7/31-22:39
     */
    public class FinalizeGC {
        public static FinalizeGC instance = null;
        public void isAlive() {
            System.out.println("I am still alive!");
        }
        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println("finalize method executed");
            FinalizeGC.instance = this;
        }
        
        public static void main(String[] args) throws Throwable {
            instance = new FinalizeGC();
    
            //对象进行第1次GC
            instance = null;
            System.gc();
            Thread.sleep(1000);//Finalizer 方法优先级很低,需要等待
            if (instance != null) {
                instance.isAlive();
            } else {
                System.out.println("I am dead! ");
            }
    
            //对象进行第2次GC
            instance = null;
            System.gc();
            Thread.sleep(1000);
            if (instance != null) {
                instance.isAlive();
            } else {
                System.out.println("I am dead! ");
            }
        }
    }

    运行结果:

    finalize method executed
    I am still alive!
    I am dead! 

    可以看到,对象可以被拯救一次(finalize  执行第一次,但是不会执行第二次)
    代码改一下,再来一次。

    /**
     * @author macfmc
     * @date 2020/7/31-22:39
     */
    public class FinalizeGC {
        public static FinalizeGC instance = null;
        public void isAlive() {
            System.out.println("I am still alive!");
        }
        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println("finalize method executed");
            FinalizeGC.instance = this;
        }
        
        public static void main(String[] args) throws Throwable {
            instance = new FinalizeGC();
            //对象进行第1次GC
            instance = null;
            System.gc();
            // Thread.sleep(1000);//Finalizer 方法优先级很低,需要等待
            if (instance != null) 
                instance.isAlive();
            else 
                System.out.println("I am dead! ");
            //对象进行第2次GC
            instance = null;
            System.gc();
            // Thread.sleep(1000);
            if (instance != null) {
                instance.isAlive();
            } else {
                System.out.println("I am dead! ");
            }
        }
    }

    运行结果:

    finalize method executed
    I am dead! 
    I am dead! 

    对象没有被拯救,这个就是 finalize()方法执行缓慢,还没有完成拯救,垃圾回收器就已经回收掉了。
    所以建议大家尽量不要使用 finalize, 因为这个方法太不可靠 。 在生产中你很难控制方法的执行或者对象的调用顺序 , 建议大家忘了 finalize  方法 ! 因为在finalize  方法能做的工作,java中有更好的,比如 try-finally或者其他方式可以做得更好

    各种引用

    强引用

    一般的 Object obj = new Object() ,就属于强引用。在任何情况下,只有有强引用关联(与根可达)还在,垃圾回收器就永远不会回收掉被引用的对象。

    软引用 SoftReference

    一些有用但是并非必需,用软引用关联的对象,系统将要发生内存溢出(OuyOfMemory)之前,这些对象就会被回收(如果这次回收后还是没有足够的空间,才会抛出内存溢出)。参见代码:VM 参数 -Xms10m -Xmx10m -XX:+PrintGC
    代码演示:

    import java.lang.ref.SoftReference;
    import java.util.LinkedList;
    import java.util.List;
    
    /**
     * @author macfmc
     * @date 2020/7/31-22:56
     */
    public class gcRoot {
        public static void main(String[] args) {
            gcRoot u = new gcRoot(); //new是强引用
            SoftReference<gcRoot> userSoft = new SoftReference<>(u);
            u = null;//干掉强引用,确保这个实例只有userSoft的软引用
            System.out.println(userSoft.get());
            System.gc();//进行- -次GC垃圾回收
            System.out.println("After gc");
            System.out.println(userSoft.get());
            //往堆中填充数据,导致00M
            List<byte[]> list = new LinkedList<>();
            try {
                for (int i = 0; i < 100; ++i) {
                    System.out.println("*************" + userSoft.get());
                    list.add(new byte[1024 * 1024 * 1]); //1M的对象
                }
            } catch (Throwable e) {
                //抛出了00M异常时打印软引用对象
                System.out.println("Excepti n*************" + userSoft.get());
            }
        }
    }
    

    运行结果:

    main.java.JVM.gcRoot@4554617c
    [GC (System.gc())  3341K->856K(125952K), 0.0043645 secs]
    [Full GC (System.gc())  856K->662K(125952K), 0.0074266 secs]
    After gc
    main.java.JVM.gcRoot@4554617c
    *************main.java.JVM.gcRoot@4554617c
    *************main.java.JVM.gcRoot@4554617c
    [GC (Allocation Failure)  33072K->32502K(125952K), 0.0102263 secs]
    *************main.java.JVM.gcRoot@4554617c
    *************main.java.JVM.gcRoot@4554617c
    [GC (Allocation Failure)  64906K->64247K(125952K), 0.0123316 secs]
    [Full GC (Ergonomics)  64247K->64149K(194560K), 0.0198417 secs]

    例如,一个程序用来处理用户提供的图片。如果将所有图片读入内存,这样虽然可以很快的打开图片,但内存空间使用巨大,一些使用较少的图片浪费内存空间,需要手动从内存中移除。如果每次打开图片都从磁盘文件中读取到内存再显示出来,虽然内存占用较少,但一些经常使用的图片每次打开都要访问磁盘,代价巨大。这个时候就可以用软引用构建缓存。

    弱引用 WeakReference

    一些有用(程度比软引用更低)但是并非必需,用弱引用关联的对象,只能生存到下一次垃圾回收之前,GC 发生时,不管内存够不够,都会被回收。
    代码演示:

    import java.lang.ref.WeakReference;
    
    /**
     * @author macfmc
     * @date 2020/7/31-22:56
     */
    public class gcRoot {
        public static void main(String[] args) {
            gcRoot u = new gcRoot();
            WeakReference<gcRoot> userWeak = new WeakReference<>(u);
            u = null;//干掉强引用,确保这个实例只有userWeak的弱引用
            System.out.println(userWeak.get());
            System.gc();//进行一次GC垃圾回收
            System.out.println("After gc");
            System.out.println(userWeak.get());
        }
    }
    

    运行结果:

    main.java.JVM.gcRoot@4554617c
    [GC (System.gc())  3341K->752K(125952K), 0.0014105 secs]
    [Full GC (System.gc())  752K->625K(125952K), 0.0103507 secs]
    After gc
    null

    注意:软引用 SoftReference 和弱引用 WeakReference,可以用在内存资源紧张的情况下以及创建不是很重要的数据缓存。当系统内存不足的时候,缓存中的内容是可以被释放的。实际运用(WeakHashMap、ThreadLocal)

    虚引用 PhantomReference

    幽灵引用,最弱(随时会被回收掉)垃圾回收的时候收到一个通知,就是为了监控垃圾回收器是否正常工作

    展开全文
  • Sql小白入门(三)管理数据库对象

    千次阅读 2017-01-10 22:18:46
     数据库对象是数据库里定义的、用于存储或引用数据的对象,比如表、视图、促,序列、索引和异名。本章的内容以表为主,因为它是关系型数据库里主要、简单的数据存储形式。 2、什么是规则  规则是与数据库

        前面两篇文章都是概念性的,全部文字描述!看起来比较费劲!有的读者在看文章时是不是会睡着呢!偷笑今天我们就正式进入到数据库和表的实战中!开始下文吧!

    一、概念。

    1、什么是数据库对象

        数据库对象是数据库里定义的、用于存储或引用数据的对象,比如表、视图、促,序列、索引和异名。本章的内容以表为主,因为它是关系型数据库里最主要、最简单的数据存储形式。

    2、什么是规则

       规则是与数据库某个用户名相关联的数据库对象结婚。相应的用户名被称为规则所有人,或是关联对象组的所有人。数据库里可以有一个或多个规则。用户至于同名规则相关联,通常情况下反之亦然。一般来说,当用户创建一个对象时,就是在自己的规划里创建了它,除非明确指定在另一个规划里创建它。因此,根据在数据库里的权限,用户可以创建、操作和删除对象。规划可以只包含一个表,也可以包含无数个对象,其上限由具体的Sql实现决定。

    假设我们从管理员获得了一个数据用户名和密码,用户名是USER1,我们登录到数据库并创建一个名为EMPLOYEE_TBL的表,这时对于数据库来说,表的实际名称是USER1.EMPLOYEE_TBL,这个表的规划名是USER1,也就是这个表的所有者。这样,我们就为这个规划创建了第一个表。

    当我们访问自己所拥有的表时(在级的规划里),不必引用规划名称。举例来说,使用下面两种方式都可以引用刚才创建的表:

    MPLOYEE_TBL

    USER1.MPLOYEE_TBL

    我们当然喜欢使用第一种方法,因为它简单,需要敲击键盘的次数比较少。如果其他用户要访问这个表,就必须知道规划名称,如下所示;

    USER1.MPLOYEE_TBL

    3.表,数据的主要存储方式

    表是关系型数据库里最主要的数据存储对象,其最简单形式是由行和列组成,分别都包含着数据。表在数据库占据实际的物理空间,可以是永久的或是临时的。

    (1).列

    字段在关系型数据库也被称为列,它是表的组成部分,被设置为特定的数据类型。数据类型觉得了什么样的数据尅保存在相应的列中,从而确保了数据的完整性。

    每个数据库表都至少要包含一列、列元素在表里用于保存特定类型的数据,比如人名或地址。举例来说,姓名就可以作为顾客表里一个有效的列。

    一般来说,列的名称应该是连续的字符串,其长度在不同的Sql实现中都是由明确的规定。我们一般使用下划线作为分隔符,比如表示顾客姓名的列可以命名为CUSTOMER_NAME更好一些。这样做可以提高数据库对象的可读性。读者也可以使用其他命名规则,例如驼峰匹配,以满足特定的需求。对于一个数据库开发团队来说,明确一个命名规则,并在开发过程中严格遵守这一规则,是非常重要的。

    列中最常见的数据类型是字符串。这一数据可以保存为大写或小写字符,应该根据数据的使用方式具体选择。在大多数情况下,出于简化和一致的目的,数据是以大写存储的。如果数据库里存储的数据具有不同的大小写,我们可以根据需要利用函数把数据转化为大写或小写。

    列也可以指定为NULl或NOT NULL,当设置为NOT NULL时,表示其中必须包含数据;这是为NULL是,就表示不包含数据。NULL不是空白,而是类似一个空的字符串,在数据库中占据了一个特殊的位置。因此,如果某一个位置缺少数据,就尅使用NULL。

    (2).行

    行是数据库表里的一条记录。举例来说,顾客表里的一行数据可能包含顾客的标识号码、姓名、地址、电话号码、传真号码等。行由字段组成,表最少可以包含一行数据,也可以包含数以百万计的记录。

    二、表。

    1.CREATE TABLE命令。

    Sql里的CREATE TABLE语句用于创建表。虽然创建表的实际操作十分简单,但在执行CREATE TABLE命令之前,应该花更多的时间和精力来设计表的结构,这样可以节省反复修改表结构而浪费的时间。

    在创建表时,需要考虑以下一些基本问题。

    (1).表里会包含什么类型的数据;

    (2).表的名称是什么;

    (3).哪个(或哪些)列组成主键;

    (4).列(字段)的名称是什么;

    (5).每一列的数据类型是什么;

    (6).每一列的长度是多少;

    (7).表里的哪些列可以是NULL;

    在考虑了这些问题之后,实际的CREATE TABLE命令就很简单了!

    创建表的基本语法如下所示:

    CREATE TABLE table_name(

    field1 data_type [ not null],

    field2 data_type [ not null],

    field3 data_type [ not null],

    );

    2.命名规范

    在为对象选择名称时,特别是表和列的名称,应该让名称反应出所保存的数据。比如说,保存雇员信息的表可以命名为EMPLOYEE_TBL。列的名称也是如此,比如说,保存雇员电话号码的列,显然命名为PHONE_NUMBER是比较合适的。

    3.ALTER TABLE命令

    在表被创建之后,我们可以使用ALTER TABLE命令对其进行修改。可以调节列、删除列、修改列定义、添加和去除约束,在某些实现中可以修改表的值。ALTER TABLE命令的

    (1).修改表的元素。

    列的属性是其所包含数据的规则和行为。利用ALTER TABLE命令修改列的属性,在此“属性”的含义是:

    列的数据类型,列的长度、有效位或标度,列值能否为空。

    下面的范例是使用ALTER TABLE命令修改表EMPLOYEE_TBL的EMP_ID列,

    ALTER TABLE EMPLOYEE_TBL MODIFY EMP_ID VARCHAR(10)

    这一列定义的数据类型没有变,但是长度从9变为10。

    (2).添加列。

    如果表已经包含数据,这是添加的列就不能定义为NOT NULL,这是一条基本规则。NOT NULL意味着这一列在每条记录里都是必须包含数据。所以,在添加一条定义为NOT NULL的列时,如果现有的记录没有包含新的列所需的数据,我们就会陷入到自相矛盾的境地。

    因此,强行向表添加一列的方法如下:

    添加一列,把它定义为NULL(这一行不一定要包含数据),

    给这个新列在每条记录里都插入数据,

    把列的定义修改为NOT  NULL.

    (3).添加自动增加的列。

    有时我们需要一列的数据能够自动增加,从而让每一行都具有不同的序号。在很多情况下都需要这样做,比如数据中如果没有适合充当主键的值,或是我们想利用序列号对数据进行排序。创建自动增加的列是相当简单的。MySql提供了SERIAL方法为表生成真正的唯一值,如下所示:

    CREATE TABLE TEST_INCREMENT(

     ID SERIAL,

    TEST_NAME VARCHAR(20)

    );

    PS:列的默认属性是NULL,所以在CREATE TABLE语句里不必明确设置。但NOT NULL必须明确指定。

    (4).修改列

    在修改现有表里的列时,需要考虑很多因素。下面是修改列的一些通用规则:

    列的长度可以增加到特定数据类型所允许的最大长度;

    如果想缩短某列的长度,则长度要求这一列在表里所有数据的长度都小于或等于新长度;

    数值数据的位数可以增加;

    如果要缩短数值数据的位数,则必须要求这一列在表里所有数值的位数小于或等于新指定的位数;

    数值里的小数位数可以增加或减少;

    列的数据类型一般是可以改变的。于其它表的列,或是被其它表的列所引用,在撤销这一列时就可能发生问题。

    有些实现会限制用户使用ALTER TABLE的某些选项。举例来说,可能不允许从表里撤销列。为了绕过这种限制,我们可以撤销整个表,然后重建新的表。如果某一列是依赖于其它表的列,或是被其它表的列所引用,在撤销这一列时就可能发生问题。

    4.删除表

    删除表示一种相当简单的操作。如果使用了RESTRICT选项,并且表被视图或约束所引用,DROP语句就会返回一个错误。当使用了CASCADE选项时,删除操作会成功执行,而且全部引用视图和约束都被删除。删除表的语句如下所示:

    drop table  table_name

    PS:删除表的操作务必指向准确,在删除表时,在提交命令之前要确保指定了表的规划名或所有者,否则可能误删除其它的表。如果使用多用户账户,在删除表之前一定要确定使用了适当的用户名链接数据库。

    三、完整性约束。

    完整性约束用于确定关系型数据库里数据的准确性和一致性。在关系型数据库里,数据完整性是通过引用完整性的概念实现的,而在引用完整性里包含了很多类型。

    1.主键约束

    主键是表里一个或多个用于实现记录唯一性的字段。虽然主键通常是由一个字段构成的,但也可以由多个字段组成。举例来数,雇员的社会保险号码或雇员被分配的标识号码都可以再雇员表里作为主键。主键的作用在于表里每条记录都具有唯一值。由于在雇员表里一般不会出现多条记录表示一个雇员的情况,所以雇员的标识号码可以作为主键。主键是在创建表时指定的。

    2.唯一性约束

    唯一性约束要求表里某个字符的值在每条记录里都是唯一的,这一点与主键类似。即使我们对一个字段设置了主键约束,也可以对另一个字段设置唯一性约束,尽管它不会被当做主键使用。

    3.外键约束

    外键是子表里的一个字段,引用父表里的主键。外键约束是确保表与表之间引用完整性的主要机制。一个被定义为外键的字段用于引用另一个表里的主键。

    4. NOT NULL 约束

    NOT NULL是一个可以用于字段的约束,它不允许字段包含NULL值;话句话时候,定义NOT NULL的字段在每条记录里都必须有值。在没有指定NOT NULL时,字段默认为NULL,也就是可以是NULL值。

    5.检查约束

    检查约束用于检查输入到特定字段的数据的有效性,可以提供后端的数据库编辑,虽然编辑通常是在前端程序里完成的。一般情况下,编辑功能限制了能够输入字段或对象的值,无论这个功能是在数据库还是在前端程序里实现的。检查约束为数据提供了另一层保护。

    7.去除约束

    利用ALTER TABLE命令的DROP CONSTRAINT选项可以去除已经定义的约束。

    PS: 有效实现允许终止约束,这样我们可以选择暂时中止它,而不是从数据库里去除它,稍后还尅再启动它。

    四、实战操作

        本系列博文中使用的数据库是MySql,有关MySql的下载和安装就不多说了,直接百度就知道了!并且在实际开发中,还使用了数据库可视化工具,例如SQLyog,使用这个工具可以快速开发、管理MySql数据库。在MySql命令行中输入的命令,目前只在MySql中验证有效,其他数据库没有验证过!有关SQLyog有问题,找度娘!偷笑

    安装完MySql后,打开SQLyog,新建一个连接,输入密码,如果显示如下图所示,就表示成功了,


    1.创建数据库。

    创建数据库有两种方式。

    (1).在mySQL命令提示符下,输入如下所示命令:
    MySQL>create datebase dbname;

    其中dbname是数据库名称。一定要记得命令最后还有“;”!

    (2).在SQLyog中,在左侧列表的空白处单击鼠标右键,弹出的菜单中,选择“创建数据库”,接着会弹出如下图的对话框,填写数据库名的选项框,我们这里填写“studensManager”,其中两个选择项选择默认值即可,接着单击“创建”按钮,“studensManager”数据库就创建成功了,在左边的数据库列表里会出现“studensManager”数据库。


    创建数据库成功后,接着就可以创建表了!

    PS:

    (1).在MySql命令行中,如果我们想查看目前都有哪些数据库?该使用什么命令呢?

    show databases; 一定要记得命令最后还有“;”!可以试试不输入“;”,是什么结果!

    便会显示目前MySql中有哪些数据库。例如我本机上显示如下图所示,


    (2).如何选择进入哪个数据库呢?

    命令: use  数据库名;

    例如,我本机显示如下,

    (3).那么如何查看当前使用的数据库
    命令:mysql> select database();

    例如,我本机显示如下,

    (4).查看数据库的表信息
    命令:mysql> show tables;


    2.创建表。

    还记得创建表的命令吗?CREATE TABLE TABLE_NAME(...);

    (1).使用命令创建。
    CREATE TABLE studens(sno INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,sname VARCHAR(20),INT(3) age,sex CHAR(2),birth DATE);

    (2).在SQLyog中,左侧的导航中选择所要操作的数据库,单击鼠标右键,从弹出的快捷菜单中选择创建表命令,创建新表。


    创建表成功后,就会在Tables目录下看到表students,


    3.修改表。

    如果我们要对已经创建成功的表的列进行修改,该如何操作呢?还记得前面说的吗?

    ().使用Alter命令!

    (2).在SQLyog中,选中要修改的表,选择更改表选项,就会显示类似一个创建表的窗口,接着在这个窗口中就可以修改表了!添加或者删除某一列,


    修改完后,点击Alter按钮!


    再次刷新表,就可以看到表多了一列!修改成功!

    五、小结。

    1.创建数据库命令。

    CREATE DATABASE database_name
    现在我们希望创建一个名为 "mserver_db" 的数据库。
    我们使用下面的 CREATE DATABASE 语句:
    CREATE DATABASE mserver_db

    2.创建表命令。

    CREATE TABLE 表名称
    (
    列名称1 数据类型,
    列名称2 数据类型,
    列名称3 数据类型,
    ....
    )

    例如创建一个Persons表,

    CREATE TABLE Persons
    (
    Id_P int,
    LastName varchar(255),
    FirstName varchar(255),
    Address varchar(255),
    City varchar(255)
    )
    3.修改列。

    如需在表中添加列,请使用下列语法:
    ALTER TABLE table_name
    ADD column_name datatype
    要删除表中的列,请使用下列语法:
    ALTER TABLE table_name
    DROP COLUMN column_name
    注释:某些数据库系统不允许这种在数据库表中删除列的方式 (DROP COLUMN column_name)。
    要改变表中列的数据类型,请使用下列语法:
    ALTER TABLE table_name
    ALTER COLUMN column_name datatype

    今天就到这里!下篇文章,我们继续表的操作!详情请看,Sql小白入门(四)CRUD
















    展开全文
  • R语言面向对象指南

    千次阅读 2015-09-21 21:53:00
    面向对象指南:这一章主要介绍怎样识别和使用 R 语言的面向对象系统(以下简称 OO)。R 语言主要有三种 OO 系统(加上基本类型)。本指南的目的不是让你精通 R 语言的 OO,而是让你熟悉各种系统,并且能够准确地区分...

    原文链接:OO field guide


    面向对象指南:

    这一章主要介绍怎样识别和使用 R 语言的面向对象系统(以下简称 OO)。R 语言主要有三种 OO 系统(加上基本类型)。本指南的目的不是让你精通 R 语言的 OO,而是让你熟悉各种系统,并且能够准确地区分和使用它们。
    OO 最核心的就是类和方法的思想,类在定义对象的行为时主要是通过对象的属性以及它和其它类之间的关系。根据类的输入不同,类对方法、函数的选择也会不同。类的建造是有层次结构的:如果一个方法在子类中不存在,则使用父类中的方法;如果存在则继承父类中方法。

    三种 OO 系统在定义类和方法的时候有以下不同:

    • S3 实现的是泛型函数式 OO ,这与大部分的编程语言不同,像 Java、C++ 和 C# 它们实现的是消息传递式的 OO 。如果是消息传递,消息(方法)是传给一个对象,再由对象去决定调用哪个方法的。通常调用方法的形式是“对象名.方法名”,例如:canvas.drawRect(“blue”) 。而 S3 不同,S3 调用哪个方法是由泛型函数决定的,例如:drawRect(canvas, “blue”)。S3 是一种非正式的 OO 模式,它甚至都没有正式定义类这个概念。
    • S4 与 S3 很相似,但是比 S3 正规。S4 与 S3 的不同主要有两点:S4 对类有更加正式的定义(描述了每个类的表现形式和继承情况,并且对泛型和方法的定义添加了特殊的辅助函数);S4 支持多调度(这意味着泛型函数在调用方法的时候可以选择多个参数)。
    • Reference classes (引用类),简称 RC ,和 S3、S4有很大区别。RC 实现的是消息传递式 OO ,所以方法是属于类的,而不是函数。对象和方法之间用”$”隔开,所以调用方法的形式如:canvas$drawRect(“blue”) 。RC 对象也总是可变的,它用的不是 R 平常的 copy-on-modify 语义,而是做了部分修改。从而可以解决 S3、S4 难以解决的问题。

    还有另外一种系统,虽然不是完全的面向对象,但还是有必要提一下:

    • base types(基本类型),主要使用C语言代码来操作。它之所以重要是因为它能为其它 OO 系统提供构建块。

    以下内容从基本类型开始,逐个介绍每种 OO 系统。你将学习到怎样识别一个对象是属于哪种 OO 系统、方法的调用和使用,以及在该 OO 系统下如何创建新的对象、类、泛型和方法。本章节的结尾也有讲述哪种情况应该使用哪种系统。

    前提:

    你首先需要安装 pryr 包来获取某些函数:install.packages(“pryr”) 。

    问题:

    你是否已经了解本文要讲述的内容?如果你能准确地回答出以下问题,则可以跳过本章节了。答案请见本文末尾的问题答案

    1. 你怎样区分一个对象属于哪种 OO 系统?
    2. 如何确定基本类型(如整型或者列表)的对象?
    3. 什么是类的函数?
    4. S3 和 S4 之间的主要差异是什么? S4 和 RC 之间最主要的差异又是什么?

    文章梗概:


    基本类型:

    基本上每个 R 对象都类似于描述内存存储的 C 语言结构体,这个结构体包含了对象的所有内容(包括内存管理需要的信息,还有对象的基本类型)。基本类型并不是真的对象系统,因为只有 R 语言的核心团队才能创建新的类型。但结果新的基本类型竟然也很少见地被添加了:最近是在2011年,添加了两个你从来没在 R 里面见过的奇异类型(NEWSXP 和 FREESXP),它们能够有效地诊断出内存上的问题。在此之前,2005年为 S4 对象添加了一个特殊的基本类型(S4SXP)。

    Data structures 章节讲述了大部分普通的基本类型(原子向量和列表),但基本类型还包括 functions、environments,以及其它更加奇异的对象,如 names、calls、promises,之后你将会在本书中学到。你可以使用 typeof() 来了解对象的基本类型。但基本类型的名字在 R 中并不总是有效的,并且类型和 “is” 函数可能会使用不同的名字:

    # The type of a function is "closure"
    f <- function() {}
    typeof(f)
    #> [1] "closure"
    is.function(f)
    #> [1] TRUE
    
    # The type of a primitive function is "builtin"
    typeof(sum)
    #> [1] "builtin"
    is.primitive(sum)
    #> [1] TRUE

    你可能听过 mode() 和 storage.mode(),我建议不要使用这两个函数,因为它们只是 typeof() 返回值的别名,而且只使用与 S 语言。如果你想了解它们具体如何实现,可以去看一下它们的源代码。

    不同基本类型的函数一般都是用 C 语言编写的,在调度时使用switch语句(例如:switch(TYPEOF(x)))。尽管你可能没有写过 C 语言,但理解基本类型仍然很有必要,因为其他系统都是在此基础上的:S3 对象可以建立在所有基本类型上,S4 使用一个特殊的基本类型,而 RC 对象是 S4 和 environments(一个特殊的基本类型)的结合体。查看对象是否是一个单纯基本类型(即它不同时含 S3、S4、RC 的行为),使用 is.object(x) ,返回TRUE/FALSSE。


    S3:

    S3 是 R 语言的第一种也是最简单的一种 OO 系统。它还是唯一一种在基础包和统计包使用的 OO 系统,CRAN包中最平常使用的 OO 系统。

    识别对象、泛型函数、方法:

    你遇到的大部分对象都是 S3 对象。但不幸的是在 R 中并没有可以简单检测一个方法是否是 S3 的方法。最接近的方法就是 is.object(x) & !isS4(x),即它是一个对象,但不是 S4 对象。一个更简单的方法就是使用 pryr::otype() :

    library(pryr)
    
    df <- data.frame(x = 1:10, y = letters[1:10])
    otype(df)    # A data frame is an S3 class
    #> [1] "S3"
    otype(df$x)  # A numeric vector isn't
    #> [1] "base"
    otype(df$y)  # A factor is
    #> [1] "S3"

    在 S3,方法是属于函数的,这些函数叫做泛型函数,或简称泛型。S3 的方法不属于对象或者类。这和大部分的编程语言都不同,但它确实是一种合法的 OO 方式。

    你可以调用 UseMethod() 方法来查看某个函数的源代码,从而确定它是否是 S3 泛型。和 otype() 类似,prpy 也提供了 ftype() 来联系着一个函数(如果有的话)描述对象系统。

    mean
    #> function (x, ...) 
    #> UseMethod("mean")
    #> <bytecode: 0x24bfa50>
    #> <environment: namespace:base>
    ftype(mean)
    #> [1] "s3"      "generic"

    有些 S3 泛型,例如 [ 、sum()、cbind(),不能调用 UseMethod(),因为它们是用 C 语言来执行的。不过它们可以调用 C 语言的函数 DispatchGroup() 和 DispatchOrEval()。利用 C 代码进行方法调用的函数叫作内部泛型。可以使用 ?”internal generic” 查看。

    给定一个类,S3 泛型的工作是调用正确的 S3 方法。你可以通过 S3 方法的名字来识别(形如 generic.class())。例如,泛型 mean() 的 Date 方法为 mean.Date(),泛型print() 的向量方法为 print.factor() 。
    这也就是为什么现代风格不鼓励在函数名字里使用 “.” 的原因了。类的名字也不使用 “.” 。pryr::ftype() 可以发现这些异常,所以你可以用它来识别一个函数是 S3 方法还是泛型:

    ftype(t.data.frame) # data frame method for t()
    #> [1] "s3"     "method"
    ftype(t.test)       # generic function for t tests
    #> [1] "s3"      "generic"

    你可以调用 methods() 来查看属于某个泛型的所有方法:

    methods("mean")
    #> [1] mean.Date     mean.default  mean.difftime mean.POSIXct  mean.POSIXlt 
    #> see '?methods' for accessing help and source code
    methods("t.test")
    #> [1] t.test.default* t.test.formula*
    #> see '?methods' for accessing help and source code

    (除了在基础包里面定义的一些方法,大多数 S3 的方法都是不可见的使用 getS3method() 来阅读它们的源码。)

    你也可以列出一个给出类中包含某个方法的所有泛型:

    methods(class = "ts")
    #>  [1] aggregate     as.data.frame cbind         coerce        cycle        
    #>  [6] diffinv       diff          initialize    kernapply     lines        
    #> [11] Math2         Math          monthplot     na.omit       Ops          
    #> [16] plot          print         show          slotsFromS3   time         
    #> [21] [<-           [             t             window<-      window       
    #> see '?methods' for accessing help and source code

    你也可以从接下来的部分知道,要列出所有的 S3 类是不可能的。

    定义类和创建对象:

    S3 是一个简单而特殊的系统,它对类没有正式的定义。要实例化一个类,你只能拿一个已有的基础对象,再设置类的属性。你可以在创建类的时候使用 structure(),或者事后用 class<-():

    # Create and assign class in one step
    foo <- structure(list(), class = "foo")
    
    # Create, then set class
    foo <- list()
    class(foo) <- "foo"

    S3 对象的属性通常建立在列表或者原子向量之上(你可以用这个属性去刷新你的内存属性),你也能把函数转成 S3 对象,其他基本类型要么在 R 中很少见,要么就是该语义不能很好地在属性下运行。
    你可以通过 class() 把类看作任意的对象,也可以通过 inherits(x, “classname”) 来查看某个对象是否继承自某个具体的类。

    class(foo)
    #> [1] "foo"
    inherits(foo, "foo")
    #> [1] TRUE

    S3 对象所属于的类可以被看成是一个向量,一个通过最重要的特性来描述对象行为的向量。例如对象 glm() 的类是 c(“glm”, “lm”),它表明着广义线性模型的行为继承自线性模型。类名通常是小写的,并且应该避免使用 “.” 。否则该类名将会混淆为下划线形式的 my_class,或者 CamelCase 写法的 MyClass。

    大多数的 S3 类都提供了构造函数:

    foo <- function(x) {
      if (!is.numeric(x)) stop("X must be numeric")
      structure(list(x), class = "foo")
    }

    如果它是可用的,则你应该使用它(例如 factor() 和 data.frame())。这能确保你在创造类的时候使用正确的组件。构造函数的名字一般是和类名是相同的。

    开发者提供了构造函数之后,S3 并没有对它的正确性做检查。这意味着你可以改变现有对象所属于的类:

    # Create a linear model
    mod <- lm(log(mpg) ~ log(disp), data = mtcars)
    class(mod)
    #> [1] "lm"
    print(mod)
    #> 
    #> Call:
    #> lm(formula = log(mpg) ~ log(disp), data = mtcars)
    #> 
    #> Coefficients:
    #> (Intercept)    log(disp)  
    #>      5.3810      -0.4586
    
    # Turn it into a data frame (?!)
    class(mod) <- "data.frame"
    # But unsurprisingly this doesn't work very well
    print(mod)
    #>  [1] coefficients  residuals     effects       rank          fitted.values
    #>  [6] assign        qr            df.residual   xlevels       call         
    #> [11] terms         model        
    #> <0 rows> (or 0-length row.names)
    # However, the data is still there
    mod$coefficients
    #> (Intercept)   log(disp) 
    #>   5.3809725  -0.4585683

    如果你在之前使用过其他的 OO 语言,S3 可能会让你觉得很恶心。但令人惊讶的是,这种灵活性带来的问题很少:虽然你能改变对象的类型,但你并不会这么做。R 并不用提防自己:你可以很容易射自己的脚,只要你不把抢瞄在你的脚上并扣动扳机,你就不会有问题。

    创建新的方法和泛型:

    如果要添加一个新的泛型,你只要创建一个叫做 UseMethod() 的函数。UseMethod() 有两个参数:泛型函数的名字和用来调度方法的参数。如果第二个参数省略了,则根据第一个参数来调度方法。但是没有必要去省略 UseMethod() 的参数,你也不应该这么做。

    f <- function(x) UseMethod("f")

    没有方法的泛型是没有用的。如果要添加方法,你只需要用 generic.class 创建一个合法的函数:

    f.a <- function(x) "Class a"
    
    a <- structure(list(), class = "a")
    class(a)
    #> [1] "a"
    f(a)
    #> [1] "Class a"

    用同样的方法可以对已有的泛型添加方法:

    mean.a <- function(x) "a"
    mean(a)
    #> [1] "a"

    如你所看到的,它并没有确保类和泛型兼容的检查机制,它主要是靠编程者自己来确定自己的方法不会违反现有代码的期望。

    方法调度:

    S3 的方法调度比较简单。UseMethod() 创建一个向量或者一个函数名字(例如:paste0(“generic”, “.”, c(class(x), “default”))),并逐个查找。default 类作为回落的方法,以防其他未知类的情况。

    f <- function(x) UseMethod("f")
    f.a <- function(x) "Class a"
    f.default <- function(x) "Unknown class"
    
    f(structure(list(), class = "a"))
    #> [1] "Class a"
    # No method for b class, so uses method for a class
    f(structure(list(), class = c("b", "a")))
    #> [1] "Class a"
    # No method for c class, so falls back to default
    f(structure(list(), class = "c"))
    #> [1] "Unknown class"

    组泛型方法增加了一些复杂性,组泛型为一个函数实现复合泛型的多个方法提供了可能性。它们包含的四组泛型和函数如下:

    • Math: abs, sign, sqrt, floor, cos, sin, log, exp, …
    • Ops: +, -, *, /, ^, %%, %/%, &, |, !, ==, !=, <, <=, >=, >
    • Summary: all, any, sum, prod, min, max, range
    • Complex: Arg, Conj, Im, Mod, Re

    组泛型是相对比较先进的技术,超出了本章的范围。但是你可以通过 ?groupGeneric 查看更多相关信息。区分组泛型最关键的是要意识到 Math、Ops、Summary 和 Complex 并不是真正的函数,而是代表着函数。注意在组泛型中有特殊的变量 .Generic 提供实际的泛型函数调用。

    如果你有复数类模板的层次结构,那么调用“父”方法是有用的。要准确定义它的意义的话有点难度,但如果当前方法不存在的话它基本上都会被调用。同样的,你可以使用 ?NextMethod 查看相关信息。

    因为方法是正规的 R 函数,所以你可以直接调用它:

    c <- structure(list(), class = "c")
    # Call the correct method:
    f.default(c)
    #> [1] "Unknown class"
    # Force R to call the wrong method:
    f.a(c)
    #> [1] "Class a"

    不过这种调用的方法和改变对象的类属性一样危险,所以一般都不这样做。不要把上膛了的枪瞄在自己的脚上。使用上述方法的唯一原因是它可以通过跳过方法调用达到很大的性能改进,你可以查看性能章节查看详情。

    非 S3 对象也可以调用 S3 泛型,非内部的泛型会调用基本类型的隐式类。(因为性能上的原因,内部的泛型并不会这样做。)确定基本类型的隐式类有点难,如下面的函数所示:

    iclass <- function(x) {
      if (is.object(x)) {
        stop("x is not a primitive type", call. = FALSE)
      }
    
      c(
        if (is.matrix(x)) "matrix",
        if (is.array(x) && !is.matrix(x)) "array",
        if (is.double(x)) "double",
        if (is.integer(x)) "integer",
        mode(x)
      )
    }
    iclass(matrix(1:5))
    #> [1] "matrix"  "integer" "numeric"
    iclass(array(1.5))
    #> [1] "array"   "double"  "numeric"

    练习:

    1. 查阅 t() 和 t.test() 的源代码,并证明 t.test() 是一个 S3 泛型而不是 S3 方法。如果你用 test 类创建一个对象并用它调用 t() 会发生什么?
    2. 在 R 语言的基本类型中什么类有 Math 组泛型?查阅源代码,该方法是如何工作的?
    3. R 语言在日期时间上有两种类,POSIXct 和 POSIXlt(两者都继承自 POSIXt)。哪些泛型对于这两个类是有不同行为的?哪个泛型共享相同的行为?
    4. 哪个基本泛型定义的方法最多?
    5. UseMethod() 通过特殊的方式调用方法。请预测下列代码将会返回什么,然后运行一下,并且查看 UseMethod() 的帮助文档,推测一下发生了什么。用最简单的方式记下这些规则。
    y <-1
    g <-function(x) {
      y <-2UseMethod("g")
    }
    g.numeric <-function(x) y
    g(10)
    
    h <-function(x) {
      x <-10UseMethod("h")
    }
    h.character <-function(x) paste("char", x)
    h.numeric <-function(x) paste("num", x)
    
    h("a")
    1. 内部泛型不分配在基类类型的隐式类。仔细查阅 ?”internal generic”,为什么下面例子中的 f 和 g 的长度不一样?哪个函数可以区分 f 和 g 的行为?
    f <- function() 1
    g <- function() 2
    class(g) <- "function"
    
    class(f)
    class(g)
    
    length.function <- function(x) "function"
    length(f)
    length(g)


    S4:

    S4 工作的方式和 S3 比较相似,但它更加正式和严谨。方法还是属于函数,而不是类。但是:

    • 类在描述字段和继承结构(父类)上有更加正式的定义。
    • 方法调用可以传递多个参数,而不仅仅是一个。
    • 出现了一个特殊的运算符——@,从 S4 对象中提取 slots(又名字段)。

    所以 S4 的相关代码都存储在 methods 包里面。当你交互运行 R 程序的时候这个包都是可用的,但在批处理的模式下则可能不可用。所以,我们在使用 S4 的时候一般直接使用 library(methods) 。
    S4 是一种丰富、复杂的系统,并不是一两页纸能解释完的。所以在此我把重点放在 S4 背后的面向对象思想,这样大家就可以比较好地使用 S4 对象了。如果想要了解更多,可以参考以下文献:

    • S4 系统在 Bioconductor 中的发展历程
    • John Chambers 写的《Software for Data Analysis》
    • Martin Morgan 在 stackoverflow 上关于 S4 问题的回答

    识别对象、泛型函数和方法:

    要识别 S4 对象 、泛型、方法还是很简单的。对于 S4 对象:str() 将它描述成一个正式的类,isS4() 会返回 TRUE,prpy::otype() 会返回 “S4” 。对于 S4 泛型函数:它们是带有很好类定义的 S4 对象。
    常用的基础包里面是没有 S4 对象的(stats, graphics, utils, datasets, 和 base),所以我们要从内建的 stats4 包新建一个 S4 对象开始,它提供了一些 S4 类和方法与最大似然估计:

    library(stats4)
    
    # From example(mle)
    y <- c(26, 17, 13, 12, 20, 5, 9, 8, 5, 4, 8)
    nLL <- function(lambda) - sum(dpois(y, lambda, log = TRUE))
    fit <- mle(nLL, start = list(lambda = 5), nobs = length(y))
    
    # An S4 object
    isS4(fit)
    #> [1] TRUE
    otype(fit)
    #> [1] "S4"
    
    # An S4 generic
    isS4(nobs)
    #> [1] TRUE
    ftype(nobs)
    #> [1] "s4"      "generic"
    
    # Retrieve an S4 method, described later
    mle_nobs <- method_from_call(nobs(fit))
    isS4(mle_nobs)
    #> [1] TRUE
    ftype(mle_nobs)
    #> [1] "s4"     "method"

    用带有一个参数的 is() 来列出对象继承的所有父类。用带有两个参数的 is() 来验证一个对象是否继承自该类:

    is(fit)
    #> [1] "mle"
    is(fit, "mle")
    #> [1] TRUE

    你可以使用 getGenerics() 来获取 S4 的所有泛型函数,或者使用 getClasses() 来获取 S4 的所有类。这些类包括 S3 对 shim classes 和基本类型。另外你可以使用 showMethods() 来获取 S4 的所有方法。

    定义类和新建对象

    在 S3,你可以通过更改类的属性就可以改变任意一个对象,但是在 S4 要求比较严格:你必须使用 setClass() 定义类的声明,并且用 new() 新建一个对象。你可以用特殊的语法 class?className(例如:class?mle)找到该类的相关文档。
    S4 类有三个主要的特性:

    • 名字:一个字母-数字的类标识符。按照惯例,S4 类名称使用 UpperCamelCase 。
    • 已命名的 slots(字段),它用来定义字段名称和允许类。例如,一个 person 类可能由字符型的名称和数字型的年龄所表征:list(name = "character", age = "numeric")
    • 父类。你可以给出多重继承的多个类,但这项先进的技能增加了它的复杂性。

    slotscontains,你可以使用setOldClass()来注册新的 S3 或 S4 类,或者基本类型的隐式类。在slots,你可以使用特殊的ANY类,它不限制输入。
    S4 类有像 validity 方法的可选属性,validity 方法可以检验一个对象是否是有效的,是否是定义了默认字段值的 prototype 对象。使用?setClass查看更多细节。
    下面的例子新建了一个具有 name 字段和 age 字段的 Person 类,还有继承自 Person 类的 Employee 类。Employee 类从 Person 类继承字段和方法,并且增加了字段 boss 。我们调用 new() 方法和类的名字,还有name-values这样成对的参数值来新建一个对象。

    setClass("Person",
      slots = list(name = "character", age = "numeric"))
    setClass("Employee",
      slots = list(boss = "Person"),
      contains = "Person")
    
    alice <- new("Person", name = "Alice", age = 40)
    john <- new("Employee", name = "John", age = 20, boss = alice)

    大部分 S4 类都有一个和类名相同名字的构造函数:如果有,可以直接用它来取代 new()
    要访问 S4 对象的字段,可以用 @ 或者 slot()

    alice@age
    #> [1] 40
    slot(john, "boss")
    #> An object of class "Person"
    #> Slot "name":
    #> [1] "Alice"
    #> 
    #> Slot "age":
    #> [1] 40

    @$ 等价,slot()[] 等价)
    如果一个 S4 对象继承自 S3 类或者基本类型,它会有特殊的属性 .Data

    setClass("RangedNumeric",
      contains = "numeric",
      slots = list(min = "numeric", max = "numeric"))
    rn <- new("RangedNumeric", 1:10, min = 1, max = 10)
    rn@min
    #> [1] 1
    rn@.Data
    #>  [1]  1  2  3  4  5  6  7  8  9 10

    因为 R 是响应式编程的语言,所以它可以随时创建新的类或者重新定义现有类。这将会造成一个问题:当你在响应式地调试 S4 的时候,如果你更改了一个类,你要知道你已经把该类的所有对象都更改了。

    新建方法和泛型函数

    S4 提供了特殊的函数来新建方法和泛型。setGeneric() 将产生一个新的泛型,或者把已有函数转成泛型。

    setGeneric("union")
    #> [1] "union"
    setMethod("union",
      c(x = "data.frame", y = "data.frame"),
      function(x, y) {
        unique(rbind(x, y))
      }
    )
    #> [1] "union"

    如果你要重新创建了一个泛型,你需要调用 standardGeneric() :

    setGeneric("myGeneric", function(x) {
      standardGeneric("myGeneric")
    })
    #> [1] "myGeneric"

    S4 中的 standardGeneric() 相当于 UseMethod()


    测试的答案

    1. 要确定一个对象属于哪种面向对象系统,你可以用排除法,如果 !is.object(x) 返回 TRUE,那么它是一个基本对象。如果 !isS4(x) 返回 TRUE,那么它是一个 S3 。如果 !is(x, "refClass") 返回 TRUE, 那么它是一个 S4 ,否则它是 RC 。
    2. typeof() 来确定基本类型的对象。
    3. 泛型函数调用特殊方法的时候主要是通过它的参数输入来确定的,在 S3 和 S4 系统,方法属于泛型函数,不像其他编程语言那样属于类。
    4. S4 比 S3 更加正式,并且支持多重继承和多重调度,RC 对象的语义和方法是属于类的,而不属于函数。
    展开全文
  • 人人都是管理

    千次阅读 2018-10-08 09:14:33
    我们每个人都掌握着不同的资源,所以需要管理对象也不同。 当资源充分的时候,我们会觉得“以正确的方式做事”更重要, 比如我们被分配了某个重要任务时候,我们的目标就是做好这件事。 但是一旦资源出现...

    以前我写过一篇文章,名字叫《毕业三到五年,别让“努力”毁了你》,

    文中我将工作前三到五年,我们应该达到的目标概括为:

     

    • 确认自己职业生规划,对至少接下来五年的职业发展有很明确的规划;
    • 知识和技能上有一定的积累,在工作中可以独当一面;
    • 形成一套自己的方法论,遇到问题时候有一套自己解决问题的方法;
    • 某个领域上有较强的能力或者知识储备,并逐渐发展成改领域的专家,培养自己的“不可替代性”;
    • 有一定的管理能力,能带领小团队完成任务

     

    这个目标总体来说我觉得还是比较明确且接地气的,

    尤其是前四点,都是针对自我规划和技术业务积累上,应该没任何问题。

    但对于第五点的管理能力上,之前我认为管理这事只属于公司管理层或者团队leader,

    直到最近看了《人人都是产品经理》的纪念版,纪念版比起之前的版本多了很多延伸阅读,

    大概就是作者几年后再看自己之前写的文章,给予了一些批注和新的理解。

    其中对于“一线员工眼中的管理”这部分,作者更新了他的观点,

    他认为,所谓管理的能力,其实就是“在资源不足的情况下把事情做成”的能力。

     


     

    管理大师彼得·德鲁克曾在《有效的主管》一书中简明扼要地指出:

    “效率是‘以正确的方式做事’,而效能则是“做正确的事”。

    效率和效能不应偏废,但这并不意味着效率和效能具有同样的重要性。

    我们当然希望同时提高效率和效能,但在效率与效能无法兼得时,

    我们首先应着眼于效能,然后再设法提高效率。”

     

    我们每个人都掌握着不同的资源,所以需要管理的对象也不同。

    当资源充分的时候,我们会觉得“以正确的方式做事”更重要,

    比如我们被分配了某个重要任务时候,我们的目标就是做好这件事。

    但是一旦资源出现了瓶颈,“做正确的事”就显得更加重要了。

    比如同时有三个人请你吃明天的晚饭,这时候的资源,即你明晚的时间不够了,

    你就需要判断和谁吃饭更有价值,谁的请客可以推后。

    这就需要自己对自己的时间进行管理了。

     

    在日常产品经理工作中,资源不足通常以以下形式表现:

    1.信息不足已决策。时间有限,能力有限,每次决策前不可能掌握所有信息,做决定时总是头疼;

    2.时间不足以安排周密的计划。总是接到3个月、1个月,甚至1个礼拜完成某项的命令,应承下来后如何计划;

    3.人员不足以支持工作强度和难度。不但时间不足,可能人员也不足,能力还不够,士气又不高;

    4.资金不足以自由调配。设备要钱,人员要钱,产品推广要钱,而花这些钱的前提是公司还得赚钱......

    而把这几点推广到生活中的各方面,凡是资源,总归不足

    既然不足,就需要学会分配资源、管理资源。比如自己的时间、衣橱、工资、手机内存......

    所以,我们人人都该是管理者。

     


    我们人人都是管理者,作为一个基层管理人员,

    我们的管理对象可能只有自己的资源和自己的工作任务,

    当资源不足时候,此时我们应该怎么做呢?

     

    1.简化解决方案,关注核心和关键问题

    当我们资源不足时候,最忌讳的是通过降低质量来解决问题。

    举生活中的例子,比如我们1天内要游玩北京五个景点,

    从时间的资源上来说,要达到这个目标肯定不够。

    此时如果我们考虑走马观花似的游玩这几个地方,或许时间上刚好够,

    但是这样势必也会让我们觉得这趟旅行没意义。

    对于项目上来说,在时间、人力等不够时候要完成整个项目,

    如果通过降低质量的方式来完成,势必留下很多隐患。

    对于软件平台而言,架构设计不合理、代码混淆等问题势必需要未来用更多的资源进行重构。

    所以降低质量来完成项目,其实就是给未来的自己挖坑。

     

    此时更好的方案就是重新梳理一下问题,找到核心业务目标。

    比如把项目的方案建议书和需求规格说明书重新审视一遍,

    结合用户需求思考和权衡,哪一些功能是目前比较关注的,而哪一些功能是未来系统需要关注的。

    有必要的时候甚至可以在它们基础之上,整理出项目这一阶段的需求规格说明书来,

    保证核心业务功能能顺利完成。

     

    2.对核心问题和关键任务进行优先等级排序

    关于个人工作效率的经典之作《高效能人士的七个习惯》那本书里依据纵坐标(重要性)和横坐标(紧急性)来划分任务,有如下四种组合:

    1、重要且紧急,迅猛龙式的出击;

    2、重要但不紧急, 准备好未来的方案、策略和计划;

    3、不重要但紧急,学会说不,拒绝任何承诺;

    4、不重要也不紧急,可以研究下隔壁的妹纸为何每次遇见你都会笑的很好看;

     

    整理完核心问题和关键任务的优先级列表后,你会发现项目一半的范围进行了裁剪。

    这是一个好的趋势和结果。因为管理,缩减和明确范围,永远对问题的如期解决是有利的。

    比如对一个项目的实施,10个人有10个人的计划和结果,2个人也有2个人的计划和结果。

    当你无法争取标配的资源获得对这个项目的足够支持时,关注核心功能、在核心功能范围内进行任务优先排序。

    之后,通常困难都是一时的,规划良好的迭代实施计划,讲缩减至一半的项目范围,持续地补充完善起来。

    通过持续渐进、裁剪和分解的方式最终实现项目的成功。

     

    3.制定持续可行的迭代计划

    迭代这个单词很泛滥,所谓迭代其实就是某个目标比较大,难以一次实现,

    比如钢琴十级,需要我们不断重复练习钢琴,一步步提升自己钢琴水平,

    而每考取一个钢琴等级,类似于就是对于我们钢琴水平迭代一次,

    通过一次次迭代,一步步完成我们的目标。

     

    对于迭代来说,但很多人都不明白需要迭代什么。

    首先要明确,在那些核心的和关键的问题清单中,有哪些是需要放在持续的迭代计划当中的,不是所有的问题都需要迭代。

    比如项目成熟的功能基本都是一次开发就落地实现,如果迭代设计登陆和退出功能都要迭代就显得非常逗。

    通常需要迭代的任务都是那些比较复杂的,业界技术不是很明确或者用户未来期望也不是很明确的功能,比如权限体系、报表体系等等。

     

    而在每个迭代计划上,最好是给自己制定一个个里程碑节点,

    然后每个里程碑节点需要完成什么目标,怎样去匹配资源、怎么去安排动作,

    注意别让自己的里程碑节点偏离自己的原定目标。

     


    最后就如“人人都是产品经理”这句话一般,我觉得“人人都是管理者”。

    管理好自己时间,管理好自己资源,管理好自己职责。

    如同连自己都管理不好,怎么可能管理好一个团队。

     

     

     

     

     

    展开全文
  • JSP-隐式对象

    千次阅读 2018-01-03 16:53:37
    JSP 隐式对象是 Web 容器加载的一组类 不需要由JSP的编写者实例化,它们由JSP容器实现和管理。在所有JSP页面中都能使用内部对象。 内部对象只对表达式和Scriptlet有用,在声明中不能使用。 隐式对象的名称是 JS
  • 项目整合管理

    千次阅读 2008-12-08 23:02:00
    虽然所有的项目管理过程在某种程度上都可看成是一个整体,但在整合管理中所描述的这些过程是基本的管理知识。整合管理主要包括:项目计划开发、项目计划实施、项目综合变更控制这三个过程。这些过程彼此相互影响,...
  • 最后但并非重要的——管道定义是一个单独的*BuildConfig*对象,并且在Jenkins之外还被定义为来自简单yaml文件的OpenShift对象 哪个更好? OpenShift的另一个功能再次使您可以轻松地通过CI/CD管道部署...
  • 不论是在技术层面还是在产品层面,大数据平台环境下的权限管理工作都是一个让人伤脑筋的烫手山芋,它不仅仅是一个技术问题,还是一个业务问题,甚至还可能是一个人际沟通和权衡利益得失的哲学问题。。。所以,以下...
  • 使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。...
  • 管理

    万次阅读 2015-06-14 06:43:43
    管理思想的发展科学管理理论泰罗提出的管理制度:1. 对工人提出科学的操作方法,以便合理利用工时,提高效率。2. 在工资制度上实行差别计件制3. 对工人进行科学的选择、培训和提高4指定科学的工艺规程,并用文件形式...
  • Java实现面向对象编程

    万次阅读 2018-07-17 16:18:06
    1.1用面向对象设计电子宠物系统... 14 1.1.1为什么使用面向对象... 14 1.1.2使用面向对象进行设计... 15 1.2通过创建对象实现领养宠物功能... 17 1.2.1创建类的对象... 17 1.2.2构造方法及其重载... 23 1.2.3...
  • 面向对象的分析方法

    千次阅读 2015-05-11 21:55:16
    OOA所强调的是在系统调查资料的基础上,针对OO方法所需要的素材进行的归类分析和整理,而不是对管理业务现状和方法的分析。  OOA(面向对象的分析)模型由5个层次(主题层、对象类层、结构层、属性层和服务层)和5...
  • 面向对象分析与设计(第3版)

    千次阅读 2012-08-22 15:59:34
    面向对象分析与设计(第3版)权威精选植根于开发实践的最佳读物 (美) 布奇(Booch,G.) 等著 王海鹏,潘加宇译 ISBN978-7-121-17389-9 2012年7月出版 定价:99.00元 16开 608页 宣传语:一技术产品只有在获得...
  • 面向对象分析与设计概述

    千次阅读 2015-06-05 13:37:01
    面向对象分析与设计概述一、概述面向对象分析与设计(Object Orient Analysis & Design,简称OOAD)是现代软件企业广泛采用的一有效技术。OOAD方法要求在设计中要映射现实世界中指定问题域中的对象和实体,例如:...
  • 日期 内核版本 ... Linux进程管理与调度 我们前面提到linux有两种方法激活调度器:核心调度器和 一种是直接的, 比如进程打算睡眠或出于其他原因放弃CPU 另一种是通过周期性的机制, 以固定的频率运行, 不
  • Linux进程上下文切换过程context_switch详解 日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 ... Linux进程管理与调度 前面我们了解了linux进程调度器的设计思路和注意框架周期调度器scheduler_tick通过li
  • 后台管理系统 – 权限管理

    万次阅读 多人点赞 2018-11-21 14:23:35
    不管是开发手机APP,网站还是小程序等项目,基本上都需要一个后台管理系统的支撑。而每个后台管理系统都有一个通用的功能就是用户权限管理。最近基于Antd+React.js做了一个后台管理系统。
  • 面向对象分析与设计总结

    千次阅读 2014-06-12 21:10:46
    1、UML中常见的关系的线性表示: ... UML的重要内容可以由五类图(共9种图形)来定义:  答:用例图:用例图。  静态图:类图、对象图  行为图:状态图、活动图  交互图:时序图、协作图  
  • 设备管理信息系统

    万次阅读 多人点赞 2016-04-08 19:26:29
    设备管理软件是设备管理模式与计算机技术结合的产物,设备管理对象是研究所中各种各样的设备 中文名 设备管理系统 外文名 Equipment Management System 内 容 设备资产及技术管理,文档管理等 任 务
  • 类与对象

    千次阅读 2012-10-06 19:43:02
    、类与对象 1、面向对象的特征有哪些方面,OOP的好处  类是具备某些共同特征的实体的集合,它是一种抽象的概念,用程序设计的语言来说,类是一种抽象的数据类型,它是对所具有相同特征实体的抽象。类定义了类的...
  • 软件项目管理考前复习资料

    万次阅读 多人点赞 2018-12-12 16:53:27
    软件项目管理概述 1.实现项目目标的制约因素有: 项目范围 成本 进度计划 客户满意度 2.项目管理包括: 启动过程组 计划过程组 执行过程组 控制过程组 收尾过程组 3.什么是项目: 为了创造一个唯一的产品或者...
  • 别再找了,这就是全网全的SpringBean的作用域管理!

    千次阅读 多人点赞 2020-09-16 05:40:23
    BeanDefinition 是配方的这种思想很重要,因为这意味着,与使用类一样,也可通过一个配方创建多个对象实例。 有如下优点: 可以控制要插入到从特定 BeanDefinition 创建的对象中的各种依赖和配置值 可以控制从...
  • 4) "item" > lindex list-key 2 "item3" > lpop list-key "item2" > lrange list-key 0 -1 1) "item" 2) "item3" 3) "item" 应用场景 Redis list的应用场景非常多,也是Redis最重要的数据结构之一。 我们可以轻松地...
  • 块存储和文件存储是我们比较熟悉的两种主流的存储类型,而对象存储(Object-based Storage)是一种新的网络存储架构,基于对象存储技术的设备就是对象存储设备(Object-based Storage Device)简称OSD。  首先...
  • Spark2.1 内存管理详解

    千次阅读 2018-12-11 14:16:23
    其中最重要的优化在于动态占用机制,其规则如下: 设定基本的存储内存和执行内存区域(spark.storage.storageFraction 参数),该设定确定了双方各自拥有的空间的范围 双方的空间都不足时,则存储到硬盘;若...
  • java面向对象程序设计的六(七)大基本原则
  • javascript面向对象精华编程

    千次阅读 热门讨论 2011-12-11 09:56:11
    我觉得,Object(对象难。因为Javascript的Object模型很独特,和其他语言都不一样,初学者不容易掌握。 Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象。但是,它又不是一种...
  • 企业管理复习题库

    万次阅读 2020-09-23 07:17:37
    2020 年春季《企业管理》复习大纲(考试时间:90 分钟) 试题类型: ...2、管理的含义:管理是根据事物的客观规律,通过计划、组织、领导和控制等职能作用于管理对象,使之适应外部环境,以达到组织
  • 面向对象七大设计原则

    千次阅读 2020-06-15 23:49:37
    面向对象设计的七大原则 前言 在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,我们能要尽量根据 7 条原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。 面向...
  • 面向对象测试 面向对象的自我梳理: 类 private(封装,能用的时候尽量用)【不可以用于修饰类】 public protected (子类可用)【不能用于修饰类】 默认 成员变量,类变量(static) 成员方法,类方法(static...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 60,670
精华内容 24,268
关键字:

以下哪项是管理最重要的对象