精华内容
下载资源
问答
  • 1、基本类型和引用类型基本的数据类型5个:undefined,boolean,number,string,nulltypeof null; //"object" typeof undefined; //"undefined" typeof 1; //"number" typeof false //"boolean" typeof "1" //...

    1、基本类型和引用类型

    基本的数据类型有5个:undefined,boolean,number,string,null

    typeof null;   //"object"
    typeof undefined;  //"undefined"
    typeof 1;  //"number"
    typeof false  //"boolean"
    typeof "1"  //"string"

    (令人困惑的是,对null类型进行typeof操作得到的结果为“object”,然而,ECMAScript标准描述其为一个独特的类型。)

    为了便于操作基本类型值,ECMAScript还提供了三个特殊的引用类型:Boolean、Number和String,标准库提供了构造函数来封装布尔值、数字和字符串作为对象。这些类型与其他引用类型相似,同时也具有与各自的基本包装类型相应的特殊行为。实际上,每当读取一个基本类型值时,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。

    var s1 = "some text";
    var s2 = s1.substring(2);
    var s3 = new String("some text");

    但是不同于原始的字符串,String对象是一个真正的对象。

    typeof s1;  //"string"
    typeof s3;  //"object"

    这个例子中的变量s1包含一个字符串,字符串当然是基本类型值。而下一行调用了s1的substring()方法,并将返回的结果保存在s2中。我们知道,基本类型值不是对象,因此从逻辑上讲,它们不应该有方法(但是它们确实有方法)。其实,为了让我们实现这种直观的操作,后台已经自动完成了一系列的处理。当第二行代码访问s1时,访问过程处于一种读取模式,也就是从内存中读取这个字符串的值。而在读取模式中访问字符串时,后台都会自动完成下列处理:

    (1)创建String类型的一个实例。

    (2)在实例上调用指定方法。

    (3)销毁这个实例。

    可以使用以下代码表示:

    var s1 = new String("some text");
    var s2 = s1.substring(2);
    s1 = null;

    经过此番处理,基本的字符串值就变得跟对象一样了。而且,上面三个步骤也适用于Boolean和Number类型对应的布尔值和数字值。

    2、生命周期

    引用类型与基本包装类型的主要区别就是对象的生命周期。使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,只存在于这一行代码的执行期(瞬间),然后立即销毁。这就意味着我们不能在运行时为属性添加属性和方法。

    var s1 = "some text";
    s1.color = "red";
    alert(s1.color); //undefined

    当然,可以显示调用Boolean、Number和String来创建基本包装类型的对象,不过,不帮不建议这么做。对基本包装类型的实例调用typeof会返回”object”,而且所有基本包装类型的对象都会被转化为布尔值true。。

    var obj = new Object("some text");
    alert(obj instanceof String) //true

    值得注意的是,使用new调用基本包装类型的构造函数,与直接调用同名的转型函数是不一样的。

    var value = "25";
    var number = Number(value);//转型函数
    alert(typeof number) //number
    
    var obj = new Number(var); //构造函数
    alert(typeof obj) //object
    

    3.基本类型特点

    1.基本类型的值是不可变得:

    任何方法都无法改变一个基本类型的值,比如一个字符串:

    var name = 'jozo';
    name.toUpperCase(); // 输出 'JOZO'
    console.log(name); // 输出  'jozo'

    会发现原始的name并未发生改变,而是调用了toUpperCase()方法后返回的是一个新的字符串。
    再来看个:

    var person = 'jozo';
    person.age = 22;
    person.method = function(){//...};
    
    console.log(person.age); // undefined
    console.log(person.method); // undefined

    通过上面代码可知,我们不能给基本类型添加属性和方法,再次说明基本类型时不可变得;

    2.基本类型的比较是值的比较:

    只有在它们的值相等的时候它们才相等。
    但你可能会这样:

    var a = 1;
    var b = true;
    console.log(a == b);//true

    它们不是相等吗?其实这是类型转换和 == 运算符的知识了,也就是说在用==比较两个不同类型的变量时会进行一些类型转换。像上面的比较先会把true转换为数字1再和数字1进行比较,结果就是true了。 这是当比较的两个值的类型不同的时候==运算符会进行类型转换,但是当两个值的类型相同的时候,即使是==也相当于是===。

    var a = 'jozo';
    var b = 'jozo';
    console.log(a === b);//true

    3.基本类型的变量是存放在栈区的(栈区指内存里的栈内存)

    假如有以下几个基本类型的变量:

    var name = 'jozo';
    var city = 'guangzhou';
    var age = 22;

    那么它的存储结构如下图:

    这里写图片描述

    栈区包括了 变量的标识符和变量的值。

    4.引用类型特点

    引用类型会比较好玩有趣一些。

    javascript中除了上面的基本类型(number,string,boolean,null,undefined)之外就是引用类型了,也可以说是就是对象了。对象是属性和方法的集合。也就是说引用类型可以拥有属性和方法,属性又可以包含基本类型和引用类型。来看看引用类型的一些特性:

    1.引用类型的值是可变的

    我们可为为引用类型添加属性和方法,也可以删除其属性和方法,如:

    var person = {};//创建个控对象 --引用类型
    person.name = 'jozo';
    person.age = 22;
    person.sayName = function(){console.log(person.name);} 
    person.sayName();// 'jozo'
    
    delete person.name; //删除person对象的name属性
    person.sayName(); // undefined

    上面代码说明引用类型可以拥有属性和方法,并且是可以动态改变的。

    2.引用类型的值是同时保存在栈内存和堆内存中的对象

    javascript和其他语言不同,其不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间,那我们操作啥呢? 实际上,是操作对象的引用,所以引用类型的值是按引用访问的。

    准确地说,引用类型的存储需要内存的栈区和堆区(堆区是指内存里的堆内存)共同完成,栈区内存保存变量标识符和指向堆内存中该对象的指针,也可以说是该对象在堆内存的地址。
    假如有以下几个对象:

    var person1 = {name:'jozo'};
    var person2 = {name:'xiaom'};
    var person3 = {name:'xiaoq'};

    则这三个对象的在内存中保存的情况如下图:

    这里写图片描述

    3.引用类型的比较是引用的比较

    var person1 = '{}';
    var person2 = '{}';
    console.log(person1 == person2); // true

    上面讲基本类型的比较的时候提到了当两个比较值的类型相同的时候,相当于是用 === ,所以输出是true了。再看看:

    var person1 = {};
    var person2 = {};
    console.log(person1 == person2); // false

    可能你已经看出破绽了,上面比较的是两个字符串,而下面比较的是两个对象,为什么长的一模一样的对象就不相等了呢?

    别忘了,引用类型时按引用访问的,换句话说就是比较两个对象的堆内存中的地址是否相同,那很明显,person1和person2在堆内存中地址是不同的:

    这里写图片描述

    所以这两个是完全不同的对象,所以返回false;

    5.简单赋值

    在从一个变量向另一个变量赋值基本类型时,会在该变量上创建一个新值,然后再把该值复制到为新变量分配的位置上:

    var a = 10;
    var b = a;
    
    a ++ ;
    console.log(a); // 11
    console.log(b); // 10

    此时,a中保存的值为 10 ,当使用 a 来初始化 b 时,b 中保存的值也为10,但b中的10与a中的是完全独立的,该值只是a中的值的一个副本,此后,这两个变量可以参加任何操作而相互不受影响。

    这里写图片描述

    也就是说基本类型在赋值操作后,两个变量是相互不受影响的。

    6.对象引用

    当从一个变量向另一个变量赋值引用类型的值时,同样也会将存储在变量中的对象的值复制一份放到为新变量分配的空间中。前面讲引用类型的时候提到,保存在变量中的是对象在堆内存中的地址,所以,与简单赋值不同,这个值的副本实际上是一个指针,而这个指针指向存储在堆内存的一个对象。那么赋值操作后,两个变量都保存了同一个对象地址,则这两个变量指向了同一个对象。因此,改变其中任何一个变量,都会相互影响:

    var a = {}; // a保存了一个空对象的实例
    var b = a;  // a和b都指向了这个空对象
    
    a.name = 'jozo';
    console.log(a.name); // 'jozo'
    console.log(b.name); // 'jozo'
    
    b.age = 22;
    console.log(b.age);// 22
    console.log(a.age);// 22
    
    console.log(a == b);// true

    它们的关系如下图:

    这里写图片描述

    因此,引用类型的赋值其实是对象保存在栈区地址指针的赋值,因此两个变量指向同一个对象,任何的操作都会相互影响。

    展开全文
  • 基本数据类型引用数据类型的区别

    千次阅读 多人点赞 2018-03-18 21:35:18
    根据这篇文章可以理解一下java中的基本数据类型与引用数据类型的区别,写的很好!javascript中基本数据类型和引用数据... 当我们把变量赋值给一个变量时,解析器首先要确认的就是这个值是基本类型值还是引用类型值...

    根据这篇文章可以理解一下java中的基本数据类型与引用数据类型的区别,写的很好!

    javascript中基本数据类型和引用数据类型的区别

    1、基本数据类型和引用数据类型

      ECMAScript包括两个不同类型的值:基本数据类型和引用数据类型。

      基本数据类型指的是简单的数据段,引用数据类型指的是有多个值构成的对象。

      当我们把变量赋值给一个变量时,解析器首先要确认的就是这个值是基本类型值还是引用类型值。

    2、常见的基本数据类型:

      Number、String 、Boolean、Null和Undefined。基本数据类型是按值访问的,因为可以直接操作保存在变量中的实际值。示例:

      var a = 10;

      var b = a;

      b = 20;

      console.log(a); // 10值

      上面,b获取的是a值得一份拷贝,虽然,两个变量的值相等,但是两个变量保存了两个不同的基本数据类型值。

      b只是保存了a复制的一个副本。所以,b的改变,对a没有影响。

      下图演示了这种基本数据类型赋值的过程:

         

    3、引用类型数据:

      也就是对象类型Object type,比如:Object 、Array 、Function 、Data等。

      javascript的引用数据类型是保存在堆内存中的对象。

      与其他语言的不同是,你不可以直接访问堆内存空间中的位置和操作堆内存空间。只能操作对象在栈内存中的引用地址。

      所以,引用类型数据在栈内存中保存的实际上是对象在堆内存中的引用地址。通过这个引用地址可以快速查找到保存中堆内存中的对象。

      var obj1 = new Object();

      var obj2 = obj1;

      obj2.name = "我有名字了";

      console.log(obj1.name); // 我有名字了

      说明这两个引用数据类型指向了同一个堆内存对象。obj1赋值给onj2,实际上这个堆内存对象在栈内存的引用地址复制了一份给了obj2,

      但是实际上他们共同指向了同一个堆内存对象。实际上改变的是堆内存对象。

      下面我们来演示这个引用数据类型赋值过程:

        

    4、总结区别

      a 声明变量时不同的内存分配: 

      1)原始值:存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置

        这是因为这些原始类型占据的空间是固定的,所以可将他们存储在较小的内存区域 – 栈中。这样存储便于迅速查寻变量的值。

      2)引用值:存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存地址。

         这是因为:引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。

         地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响。

       b 不同的内存分配机制也带来了不同的访问机制
       
      1)在javascript中是不允许直接访问保存在堆内存中的对象的,所以在访问一个对象时,
        首先得到的是这个对象在堆内存中的地址,然后再按照这个地址去获得这个对象中的值,这就是传说中的 按引用访问
      2)而原始类型的值则是可以直接访问到的。
      
      c 复制变量时的不同
      
      1)原始值:在将一个保存着原始值的变量复制给另一个变量时,会将原始值的副本赋值给新变量, 此后这两个变量是完全独立的,他们只是拥有相同的value而已。
      2)引用值:在将一个保存着对象内存地址的变量复制给另一个变量时,会把这个内存地址赋值给新变量,
        也就是说这两个变量都指向了堆内存中的同一个对象,他们中任何一个作出的改变都会反映在另一个身上。
        (这里要理解的一点就是,复制对象时并不会在堆内存中新生成一个一模一样的对象,只是多了一个保存指向这个对象指针的变量罢了)。 多了一个指针
     
       d 参数传递的不同(把实参复制给形参的过程
      
      首先我们应该明确一点:ECMAScript中所有函数的参数都 是按值来传递的。
      但是为什么涉及到原始类型与引用类型的值时仍然有区别呢?还不就是因为内存分配时的差别。  
      1)原始值:只是把变量里的值传递给参数,之后参数和这个变量互不影响。
      2)引用值:对象变量它里面的值是这个对象在堆内存中的内存地址,这一点你要时刻铭记在心!
        因此它传递的值也就是这个内存地址,这也就是为什么函数内部对这个参数的修改会体现在外部的原因了,因为它们都指向同一个对象。

    转载自大佬的博客:https://www.cnblogs.com/cxying93/p/6106469.html

    展开全文
  • Java基本类型和引用类型是什么?每种基本类型都占多少位多少字节?以及Java内存管理相关知识整理,栈内存,堆内存,方法区,内存泄漏,垃圾回收机制等。

    Java基本类型和引用类型是什么?每种基本类型都占多少位多少字节?详见下表


    基本类型

    整型

    byte

    1字节

    -27~27-1   -128~127

    short

    2字节

    -215~215-1  -32,768~32,767 (3万多)

    int

    4字节

    -231~231-1  (10位  超过20亿)

    long

    8字节

    -263~263-1  

    浮点型

    float

    4字节

    32IEEE 754单精度(有效位数67位)

    double

    8字节

    64IEEE 754双精度(有效位数15位)

    Unicode编码的字符单元

    char

    2字节

    整个Unicode字符集

    真值类型

    boolean

    1字节

    True或者false

    引用类型

    class

    Object

    Object是类层次结构的根类,每个类都使用Object作为超类,所有对象(包括数组)都实现这个类的方法

    String

    String类代表字符串,Java程序中的所有字符串字面值(如"abc")都作为此类的实例来实现。

    Date

    Date表示特定的瞬间,精确到毫秒。

    Void

    Void 类是一个不可实例化的占位符类,它保持一个对代表 Java关键字voidClass对象的引用。

    对应的Class

    Integer  Long  Boolean  Byte  Character  Double  Float   Short

    接口interface

    Runnable  Cloneable  Set  List  Map  Collection  Serializable  Comparable  Iterator

     

     先大致整理了一下,随后有时间再详细分析。

    另外一些基础知识的补充:

    1. 字节  byte       位  bit           1 byte = 8 bit  (1 Byte等于 1个 8位)

    2. 基本类型,也叫内置类型,是Java中不同于类的一种特殊类型,我们在平时的开发过程中也使用最为频繁。

        基本类型分为三类:

            字符类型    char    对应的包装类    Character

            布尔类型    boolean    对应的包装类    Boolean

            数值类型   

    byte                 Byte

    short               Short

    int                    Integer

    long                 Long

    float                 Float

    double            Double

    java中的数值类型不存在无符号的,它们的取值范围是固定的,不会随着机器硬件环境或者操作系统的改变而改变。


    Java内存管理知识整理:

    1.Java的内存管理

        Java的内存管理就是对象的分配和释放问题。

        1). 分配:  内存的分配是由程序完成的,程序员需要通过关键字new 为每个对象申请内存空间,基本类型除外。所有的对象都在堆(Heap)中分配空间。

        2). 释放:  对象的释放是由垃圾回收机制(GC)决定和执行的,这样做简化了程序员的工作,但是同时却加重了JVM的工作。(因为,GC为了能够正确释放对象,GC必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。)

    2.什么叫java的内存泄露

        在java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连(也就是说仍存在该内存对象的引用);其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。

    3.垃圾回收机制

    (1). 什么叫垃圾回收机制?
    垃圾回收是一种动态存储管理技术,它自动地释放不再被程序引用的对象,按照特定的垃圾收集算法来实现资源自动回收的功能。当一个对象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用,以免造成内存泄露。

    (2). java的垃圾回收有什么特点?
    java语言不允许程序员直接控制内存空间的使用。内存空间的分配和回收都是由JRE负责在后台自动进行的,尤其是无用内存空间的回收操作(garbagecollection,也称垃圾回收),只能由运行环境提供的一个超级线程进行监测和控制。


    (3). 垃圾回收器什么时候会运行? 
    一般是在CPU空闲或空间不足时自动进行垃圾回收,而程序员无法精确控制垃圾回收的时机和顺序等。

    (4). 什么样的对象符合垃圾回收条件?
    当没有任何获得线程能访问一个对象时,该对象就符合垃圾回收条件。

    (5). 垃圾回收器是怎样工作的?
    垃圾回收器如发现一个对象不能被任何活线程访问时,他将认为该对象符合删除条件,就将其加入回收队列,但不是立即销毁对象,何时销毁并释放内存是无法预知的。垃圾回收不能强制执行,然而java提供了一些方法(如:System.gc()方法),允许你请求JVM执行垃圾回收,而不是要求,虚拟机会尽其所能满足请求,但是不能保证JVM从内存中删除所有不用的对象。

    (6). 一个java程序能够耗尽内存吗? 
    可以。垃圾收集系统尝试在对象不被使用时把他们从内存中删除。然而,如果保持太多活的对象,系统则可能会耗尽内存。垃圾回收器不能保证有足够的内存,只能保证可用内存尽可能的得到高效的管理。

    (7). 如何显示的使对象符合垃圾回收条件? 

      a.空引用:当对象没有对他可到达引用时,他就符合垃圾回收的条件。也就是说如果没有对他的引用,删除对象的引用就可以达到目的,因此我们可以把引用变量设置为null,来符合垃圾回收的条件。

        StringBuffer sb = new StringBuffer("hello");
        System.out.println(sb);
        sb= null;

      b.重新为引用变量赋值:可以通过设置引用变量引用另一个对象来解除该引用变量与一个对象间的引用关系。 

        StringBuffer sb1 = new StringBuffer(“hello”);
        StringBuffer sb2 = new StringBuffer(“goodbye”); 
        System.out.println(sb1);
        sb1=sb2;//此时”hello”符合回收条件


      c.方法内创建的对象:所创建的局部变量仅在该方法的作用期间内存在。一旦该方法返回,在这个方法内创建的对象就符合垃圾收集条件。有一种明显的例外情况,就是方法的返回对象。

        public static void main(String[] args) {
            Date d = getDate();
            System.out.println("d="+d);
        }
        private static Date getDate() {
            Date d2 = new Date();
            StringBuffer now = new StringBuffer(d2.toString());
            System.out.println(now);
            return d2;
        }


      d.隔离引用:这种情况中,被回收的对象仍具有引用,这种情况称作隔离岛。若存在这两个实例,他们互相引用,并且这两个对象的所有其他引用都删除,其他任何线程无法访问这两个对象中的任意一个。也可以符合垃圾回收条件。

        public class Island {
            Island i;
            public static void main(String[] args) {
                Island i2 = new Island();
                Island i3 = new Island();
                Island i4 = new Island();
                i2. i =i3;
                i3. i =i4;
                i4. i =i2;
                i2= null;
                i3= null;
                i4= null;
            }
        }

    (8). 垃圾收集前进行清理——finalize()方法 

    java提供了一种机制,使你能够在对象刚要被垃圾回收之前运行一些代码。这段代码位于名为finalize()的方法内,所有类从Object类继承这个方法。由于不能保证垃圾回收器会删除某个对象。因此放在finalize()中的代码无法保证运行。因此建议不要重写finalize()。

    4. JVM的内存区域组成
    java把内存分两种:
      栈内存
        在函数中定义的基本类型变量(即基本类型的局部变量)和对象的引用变量(即对象的变量名)都在函数的栈内存中分配
      堆内存
        堆内存用来存放由new创建的对象和数组以及对象的实例变量(即全局变量)

    在函数(代码块)中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量所分配的内存空间;
    在堆中分配的内存由java虚拟机的自动垃圾回收器来管理

    堆和栈的优缺点

    堆的优势  是可以动态分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的。
            缺点  要在运行时动态分配内存,存取速度较慢;

    栈的优势  存取速度比堆要快,仅次于直接位于CPU中的寄存器。另外,栈数据可以共享。
            缺点  存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。

    此外补充一下java中还有的一个方法区
    方法区中主要存储所有对象数据共享区域,存储静态变量和普通方法、静态方法、常量、字符串常量(严格说存放在常量池,堆和栈都有)等类信息,说白了就是保存类的模板。

    // 其他地方看到的,顺便放出来参考下。
    堆区:
    a.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
    b.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
    栈区:
    a.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
    b.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
    c.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
    方法区:
    a.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
    b.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。

    5.java中数据在内存中是如何存储的

    a)基本数据类型


    java的基本数据类型共有8种,即int,short,long,byte,float,double,boolean,char(注意,并没有String的基本类型 )。这种类型的定义是通过诸如int a = 3;long b = 255L;的形式来定义的。如int a = 3;这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。

    另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。比如: 我们同时定义:

    int a = 3;
    int b =3;

    编译器先处理 int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b这个引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。

    定义完a与b的值后,再令a = 4;那么,b不会等于4,还是等于3。在编译器内部,遇到时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。

    b)对象

    在java中,创建一个对象包括对象的声明和实例化两步,下面用一个例题来说明对象的内存模型。假设有类Rectangle定义如下:

        public class Rectangle {
            double width;
            double height;
            public Rectangle( double w, double h){
                w = width;
                h = height;
            }
        }

        (1)声明对象时的内存模型 用Rectangle rect;声明一个对象rect时,将在栈内存为对象的引用变量rect分配内存空间,但Rectangle的值为空,称rect是一个空对象。空对象不能使用,因为它还没有引用任何”实体”。 

        (2)对象实例化时的内存模型 当执行rect=new Rectangle(3,5);时,会做两件事:在堆内存中为类的成员变量width,height分配内存,并将其初始化为各数据类型的默认值;接着进行显式初始化(类定义时的初始化值);最后调用构造方法,为成员变量赋值。返回堆内存中对象的引用(相当于首地址)给引用变量rect,以后就可以通过rect来引用堆内存中的对象了。

    c)创建多个不同的对象实例

    一个类通过使用new运算符可以创建多个不同的对象实例,这些对象实例将在堆中被分配不同的内存空间,改变其中一个对象的状态不会影响其他对象的状态。例如:

        Rectangle r1 = new Rectangle(3, 5);
        Rectangle r2 = new Rectangle(4, 6);

    此时,将在堆内存中分别为两个对象的成员变量 width 、 height 分配内存空间,两个对象在堆内存中占据的空间是互不相同的。如果有:

        Rectangle r1 = new Rectangle(3, 5);
        Rectangle r2 = r1;

    则在堆内存中只创建了一个对象实例,在栈内存中创建了两个对象引用,两个对象引用同时指向一个对象实例。

    d)包装类

    基本类型都有对应的包装类:如int对应Integer类,double对应Double类等,基本类型的定义都是直接在栈中,如果用包装类来创建对象,就和普通对象一样了。例如:int i=0;i直接存储在栈中。Integer i(i此时是对象)= new Integer(5);这样,i对象数据存储在堆中,i的引用存储在栈中,通过栈中的引用来操作对象。

    e)String

    String是一个特殊的包装类数据。可以用以下两种方式创建:String str = new String(“abc”);String str = “abc”; 第一种创建方式,和普通对象的的创建过程一样; 第二种创建方式,java内部将此语句转化为以下几个步骤: 

    (1)先定义一个名为str的对String类的对象引用变量:String str; 

    (2)在栈中查找有没有存放值为”abc”的地址,如果没有,则开辟一个存放字面值为”abc” 地址,接着创建一个新的String类的对象o,并将o的字符串值指向这个地址,而且在栈 这个地址旁边记下这个引用的对象o。如果已经有了值为”abc”的地址,则查找对象o,并 回o的地址。

     (3)将str指向对象o的地址。 值得注意的是,一般String类中字符串值都是直接存值的。但像String str = “abc”;这种 合下,其字符串值却是保存了一个指向存在栈中数据的引用。 为了更好地说明这个问题,我们可以通过以下的几个代码进行验证。

        String str1 = "abc";
        String str2 = "abc";
        System.out.println(s1==s2);//true

    注意,这里并不用 str1.equals(str2);的方式,因为这将比较两个字符串的值是否相等。==号,根据JDK的说明,只有在两个引用都指向了同一个对象时才返回真值。而我们在这里要看的是,str1与str2是否都指向了同一个对象。 我们再接着看以下的代码。

        String str1 = new String("abc");
        String str2 = "abc";
        System.out.println(str1 == str2);//false

    创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。 以上两段代码说明,只要是用new()来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享。

    f)数组

    当定义一个数组,int x[];或int[] x;时,在栈内存中创建一个数组引用,通过该引用(即数组名)来引用数组。x=new int[3];将在堆内存中分配3个保存 int型数据的空间,堆内存的首地址放到栈内存中,每个数组元素被初始化为0。

    g)静态变量

    用static的修饰的变量和方法,实际上是指定了这些变量和方法在内存中的”固定位置”-static storage,可以理解为所有实例对象共有的内存空间。static变量有点类似于C中的全局变量的概念;静态表示的是内存的共享,就是它的每一个实例都指向同一个内存地址。把static拿来,就是告诉JVM它是静态的,它的引用(含间接引用)都是指向同一个位置,在那个地方,你把它改了,它就不会变成原样,你把它清理了,它就不会回来了。

    那静态变量与方法是在什么时候初始化的呢?对于两种不同的类属性,static属性与instance属性,初始化的时机是不同的。instance属性在创建实例的时候初始化,static属性在类加载,也就是第一次用到这个类的时候初始化,对于后来的实例的创建,不再次进行初始化。

    我们常可看到类似以下的例子来说明这个问题:

        class Student{
            static int numberOfStudents =0;
            Student()
            {
                numberOfStudents ++;
            }
        }

    每一次创建一个新的Student实例时,成员numberOfStudents都会不断的递增,并且所有的Student实例都访问同一个numberOfStudents变量,实际上intnumberOfStudents变量在内存中只存储在一个位置上。

    6.java的内存管理实例

    Java程序的多个部分(方法,变量,对象)驻留在内存中以下两个位置:即堆和栈,现在我们只关心三类事物:实例变量,局部变量和对象: 实例变量和对象驻留在堆上 局部变量驻留在栈上 让我们查看一个 java 程序,看看他的各部分如何创建并且映射到栈和堆中:

        public class Dog {
            Collar c;
            String name;
            //1.main()方法位于栈上
            public static void main(String[] args) {
                //2.在栈上创建引用变量d,但Dog对象尚未存在
                Dog d;
                //3.创建新的Dog对象,并将其赋予d引用变量
                d = new Dog();
                //4.将引用变量的一个副本传递给go()方法
                d.go(d);
            }
    
            //5.将go()方法置于栈上,并将dog参数作为局部变量
            void go(Dog dog) {
                //6.在堆上创建新的Collar对象,并将其赋予Dog的实例变量
                c = new Collar();
            }
    
            //7.将setName()添加到栈上,并将dogName参数作为其局部变量
            void setName(String dogName) {
                //8.name的实例对象也引用String对象
                name = dogName;
            }
            //9.程序执行完成后,setName()将会完成并从栈中清除,此时,局部变量dogName也会消失,尽管它所引用的String仍在堆上
        }


    7.final问题

    final使得被修饰的变量”不变”,但是由于对象型变量的本质是”引用”,使得”不变”也有了两种含义:引用本身的不变和引用指向的对象不变。 引用本身的不变:

        final StringBuffer a = new StringBuffer("immutable");
        final StringBuffer b = new StringBuffer("not immutable");
        a=b;//编译期错误
        final StringBuffer a = new StringBuffer("immutable");
        final StringBuffer b = new StringBuffer("not immutable");
        a=b;//编译期错误

    引用指向的对象不变:

        final StringBuffer a = new StringBuffer("immutable");
        a.append("broken!");//编译通过
        final StringBuffer a = new StringBuffer("immutable");
        a.append("broken!");//编译通过

    可见,final只对引用的”值”(也即它所指向的那个对象的内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象的变化,final是不负责的。这很类似==操作符:==操作符只负责引用的”值”相等,至于这个地址所指向的对象内容是否相等,==操作符是不管的。在举一个例子:

        public class Name {
            private String firstname;
            private String lastname;
            public String getFirstname() {
                return firstname;
            }
            public void setFirstname(String firstname) {
                this.firstname = firstname;
            }
            public String getLastname() {
                return lastname;
            }
            public void setLastname(String lastname) {
                this.lastname = lastname;
            }
        }

    编写测试方法:

        public static void main(String[] args) {
            final Name name = new Name();
            name.setFirstname("JIM");
            name.setLastname("Green");
            System.out.println(name.getFirstname()+ " " +name.getLastname());
        }
        public static void main(String[] args) {
            final Name name = new Name();
            name.setFirstname("JIM");
            name.setLastname("Green");
            System.out.println(name.getFirstname()+" "+name.getLastname());
        }

    理解final问题有很重要的含义。许多程序漏洞都基于此—-final只能保证引用永远指向固定对象,不能保证那个对象的状态不变。在多线程的操作中,一个对象会被多个线程共享或修改,一个线程对对象无意识的修改可能会导致另一个使用此对象的线程崩溃。一个错误的解决方法就是在此对象新建的时候把它声明为final,意图使得它”永远不变”。其实那是徒劳的.final还有一个值得注意的地方, 先看以下示例程序:

        class Something {
            final int i ;
            public void doSomething() {
                System. out .println( "i = " + i );
            }
        }
        class Something {
            final int i;
            public void doSomething() {
                System.out.println("i = " + i);
            }
        }

    对于类变量,java虚拟机会自动进行初始化。如果给出了初始值,则初始化为该初始值。如果没有给出,则把它初始化为该类型变量的默认初始值。但是对于用final修饰的类变量,虚拟机不会为其赋予初值,必须在constructor(构造器)结束之前被赋予一个明确的值。可以修改为”final int i = 0;”。

    8.如何把程序写得更健壮

    (1)尽早释放无用对象的引用。 好的办法是使用临时变量的时候,让引用变量在退出活动域后,自动设置为null,暗示垃圾收集器来收集该对象,防止发生内存泄露。对于仍然有指针指向的实例,jvm就不会回收该资源,因为垃圾回收会将值为null的对象作为垃圾,提高GC回收机制效率;
    (2)定义字符串应该尽量使用String str=”hello”;的形式,避免使用String str = new String(“hello”);的形式。因为要使用内容相同的字符串,不必每次都new一个String。例如我们要在构造器中对一个名叫s的String引用变量进行初始化,把它设置为初始值,应当这样做:

        public class Demo {
            private String s;
            public Demo() {
                s = "Initial Value";
            }
        }
        public class Demo {
            private String s;
            …
            public Demo {
                s = "Initial Value";
            }
            …
        }
        // 而非 
        s =  new  String("Initial Value");
        s = new String("Initial Value");

    后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为String对象不可改变,所以对于内容相同的字符串,只要一个String对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象,他们的String类型属性s都指向同一个对象。
    (3)我们的程序里不可避免大量使用字符串处理,避免使用String,应大量使用StringBuffer,因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象,请看下列代码;

        String s = "Hello";
        s = s + " world!";
        String s = "Hello";
        s = s + " world!";

    在这段代码中,s原先指向一个String对象,内容是”Hello”,然后我们对s进行了+操作,那么s所指向的那个对象是否发生了改变呢?答案是没有。这时,s不指向原来那个对象了,而指向了另一个String对象,内容为”Hello world!”,原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了。
    通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用String来代表字符串的话会引起很大的内存开销。因为String对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个String对象来表示。这时,应该考虑使用StringBuffer类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。
    (4)尽量少用静态变量,因为静态变量是全局的,GC不会回收的;
    (5)尽量避免在类的构造函数里创建、初始化大量的对象,防止在调用其自身类的构造器时造成不必要的内存资源浪费,尤其是大对象,JVM会突然需要大量内存,这时必然会触发GC优化系统内存环境;显示的声明数组空间,而且申请数量还极大。 以下是初始化不同类型的对象需要消耗的时间:

    运算操作

    示例

    标准化时间

    本地赋值

    i = n

    1.0

    实例赋值

    this.i = n

    1.2

    方法调用

    Funct()

    5.9

    新建对象

    New Object()

    980

    新建数组

    New int[10]

    3100




    从表中可以看出,新建一个对象需要980个单位的时间,是本地赋值时间的980倍,是方法调用时间的166倍,而新建一个数组所花费的时间就更多了。
    (6)尽量在合适的场景下使用对象池技术以提高系统性能,缩减缩减开销,但是要注意对象池的尺寸不宜过大,及时清除无效对象释放内存资源,综合考虑应用运行环境的内存资源限制,避免过高估计运行环境所提供内存资源的数量。
    (7)大集合对象拥有大数据量的业务对象的时候,可以考虑分块进行处理,然后解决一块释放一块的策略。
    (8)不要在经常调用的方法中创建对象,尤其是忌讳在循环中创建对象。可以适当的使用hashtable,vector创建一组对象容器,然后从容器中去取那些对象,而不用每次new之后又丢弃。
    (9)一般都是发生在开启大型文件或跟数据库一次拿了太多的数据,造成Out Of Memory Error的状况,这时就大概要计算一下数据量的最大值是多少,并且设定所需最小及最大的内存空间值。
    (10)尽量少用finalize函数,因为finalize()会加大GC的工作量,而GC相当于耗费系统的计算能力。

    (11)不要过滥使用哈希表,有一定开发经验的开发人员经常会使用hash表(hash表在JDK中的一个实现就是HashMap)来缓存一些数据,从而提高系统的运行速度。比如使用HashMap缓存一些物料信息、人员信息等基础资料,这在提高系统速度的同时也加大了系统的内存占用,特别是当缓存的资料比较多的时候。其实我们可以使用操作系统中的缓存的概念来解决这个问题,也就是给被缓存的分配一个一定大小的缓存容器,按照一定的算法淘汰不需要继续缓存的对象,这样一方面会因为进行了对象缓存而提高了系统的运行效率,同时由于缓存容器不是无限制扩大,从而也减少了系统的内存占用。现在有很多开源的缓存实现项目,比如ehcache、oscache等,这些项目都实现了FIFO 、MRU等常见的缓存算法。


    本篇文章是根据查阅一些资料和技术博客后所整理完成的,如有描述不准确的,请留言提出。以免误导大家。

    展开全文
  • js数据类型(基本数据类型 | 引用数据类型

    千次阅读 多人点赞 2020-08-06 16:46:45
    引用数据类型:Object(在JS中除了基本数据类型以外的都是对象,数据是对象,函数是对象,正则表达式是对象) 1、基本数据类型(六种)存放在栈中 基本数据类型是指存放在栈中的简单数据段,数据大小确定,内存...

    一、数据类型

    JS分两种数据类型:

    基本数据类型:**Number、String、Boolean、Null、 Undefined、Symbol(ES6),**这些类型可以直接操作保存在变量中的实际值。

    引用数据类型Object(在JS中除了基数据类型以外的都是对象,数据是对象,函数是对象,正则表达式是对象)

    1、基本数据类型(六种)存放在栈中

    基本数据类型是指存放在中的简单数据段,数据大小确定,内存空间大小可以分配,它们是直接按值存放的,所以可以直接按值访问

    • 1、Number数值类型
      Number类型包含整数和浮点数(浮点数数值必须包含一个小数点,且小数点后面至少有一个数字)两种值

    注意:在js中浮点数的精度是17位,计算是二进制的计算数 据,所以得到的不是整数

    var num1 = 0.1;
    
    var num2 = 0.2;
    
    console.log(num1 + num2);   //0.30000000000000004
    

    NaN:非数字类型,属于数值型基本数据类型

    特点:
    1):设计任何的NaN操纵都会返回NaN
    console.log('ab'/10); // NaN
    2) NaN不等于自身。
    console.log(NaN == NaN);// false;

    判断是否是Number类型

    1、isNaN:判断是否是一个非数字类型,传入的非数字类型,返回true,否则返回false
    注意:传入的参数首先会被转化为数值,如果参数类型为对象类型,先调用valueOf()方法,再确定该方法返回的值是否可以转换为数值类型,如果不能,再调用toString()方法,再确定返回值
    2、typeof
    console.log(typeof 12) //Number

    数值类型的转换:
    Number():可以用于任何的数据类型
    parseInt:提取 整数数值
    paseFloat:提取浮点数值

    • 2、String 字符串类型

    特点:
    1、字符串的单引号和双引号作用效果一样
    2、字符串有length属性,可以取得字符串的长度
    3、字符串的值是不可变的,所以很多的字符串的api不会改变原字符串值

    字符串转换:
    String():适用于任何的数据类型(null -> null undefined -> undefined)
    toString()nullundefined没有这个功能
    console.log(null.toString()); //error 报错

    • 3、Boolean 布尔类型
      该类型只有两个值:truefalse
    转换为`boolean`:    `Boolean()`
    Boolean(undefined):false
    
    Boolean(null):false
    
    Boolean(非空对象包括空数组[]和空对象{}):true
    
    Boolean(0): true || Boolean(0NaN):false
    
    Boolean(非空包括空格字符串):true || Boolean(''):false
    
    [注意]true不一定等于1false也不一定等于0
    

    出现场景:
    (1)条件语句导致执行的隐士类转换
    (2)字面量或变量定义

    类型转换:
    Number(true): 1     ||   Number(false) : 0
    
    String(true):'true'      ||   String(false):'false'
    
    • 4、Null 空对象指针类型
      如果定了一个对象,初始化可以为null,因为null的基本类型是Null,在if语句中默认转化为false,在和数值计算默认为0
      出现场景:对象不存在
    类型转换:
    Booleam(null)  false
    
    Number(num)  0
    
    String(null)  'null'
    

    Number(null) // 0

    • 5、Undefined
      申明了变量但是没有初始化,默认为undefined,在if语句中默认转化为false,

    undefined:表示‘缺少值’,就是应该有一个值,但是没有定义,以下用法是典型的出现undefined情况
    (1)变量被申明,等于undefined
    (2)调用函数时,应该提供的参数没有提供,该参数等于undefined
    (3)对象没有赋值的属性,该属性值为undefined
    (4)函数没有返回值,默认返回undefined

    类型转换:
    Boolean(undefined):  false
     
    Number(undefined):  NaN
    
    String(undefined):  'undefined'
    
    • 6、Symbol
      ES6新增的一个基本数据类型,表示唯一性
     let id1 = Symbol('id');
     let id2 = Symbol('id');
     console.log(id1 == id2);  //false
    

    Symbol属性类型不支持for...inObject.keys()

     let id = Symbol("id");
     let obj = {
      [id]:'symbol'
     };
     for(let option in obj){
         console.log(obj[option]); //空
     }
    

    但是也能有方法去访问:Object.getOwnPropertySymbols
    方法会返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。

    console.log(Object.getOwnPropertySymbols(obj))
    // [ Symbol(c) ]
    
    • 介绍两个Symbol.forSymbol.keyFor
      (1)、Symbol.for :方法根据给到的键key来运行时的symbol注册表中找到对应的symbol,如果找到了,则返回它,否则新增一个与该键关联的symbol,并放入全局的symbol,这个Symbol值可以是被二次用到的
      返回值:
      返回由给定的 key 找到的 symbol,否则就是返回新创建的 symbol。
    Symbol.for("foo"); // 创建一个 symbol 并放入 symbol 注册表中,键为 "foo"
    Symbol.for("foo"); // 从 symbol 注册表中读取键为"foo"的 symbol
    
    Symbol.for("bar") === Symbol.for("bar"); // true,证明了上面说的
    Symbol("bar") === Symbol("bar"); // false,Symbol() 函数每次都会返回新的一个 symbol
    
    为了防止冲突,最好给你要放入 symbol 注册表中的 symbol 带上键前缀。
    Symbol.for("mdn.foo");
    Symbol.for("mdn.bar");
    
    

    (2)、Symbol.keyFor
    方法用来获取 symbol 注册表中与某个 symbol 关联的键。

    var globalSym = Symbol.for("foo"); 
    Symbol.keyFor(globalSym); // "foo"
    

    2、引用数据类型

    引用数据类型也叫对象数据类型,包括function,object,array,date,RegExp等可以可以使用new创建的数据,又叫对象类型,他们是存放在(heap)内存中的数据
    特点:

    • 引用类型的值可以改变
    • 引用数据类型可以添加属性和方法
    • 引用数据类型的赋值是对象引用
    • 引用类型的比较是引用的比较
    • 引用类型是同时保存在栈区中和堆区中的,引用类型的存储需要在内存的栈区和堆区中共同完成,栈区保存变量标识符和指向堆内存的地址

    注意:在引用类型赋值时对象的引用,所以从一个变量向另一个变量赋值引用类型的值时,同样会将存在在变量中的对象的值赋值一份到新的变量分配的空间,引用类型保存在变量中的时对象在堆存在的地址,所以与基本数据类型的简单赋值不同,这个值的副本实际上时一个指针,而这个指针指向储存在堆内存的一个对象,那么赋值操作后,两个变量都保存了同一个对象的地址,而这个地址都指向同一个对象,因此改变其中任何一个变量,都会影响
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jbmIM5T9-1596703084649)(images/screenshot_1595230430200.png)]

    在ECMAScript中,Object类型是所有它的实例的基础

    Object的每个实例都具有下列的属性和方法:

    • constructor:构造函数
    • hasOwnProperty(proertyName)
      用于检查给定的属性在当前对象实例(而不是实例的原型)中是否存在。
    • isPropertyOf(Object)
      用于检查其原型链的对象是否存在于指定对象的实例中,是则返回true,否则返回false。
    var a = {} function Person() {} 
    
    var p1 = new Person() // 继承自原来的原型,但现在已经无法访问  
    
    var Person.prototype = a var p2 = new Person() // 继承a  
    
    console.log(a.isPrototypeOf(p1)) // false a是不是p1的原型 
    
     console.log(a.isPrototypeOf(p2)) // true a是不是p2的原型  
    
    console.log(Object.prototype.isPrototypeOf(p1)) // true  
    
    console.log(Object.prototype.isPrototypeOf(p2)) // true
    
    
    • propertyIsEnumerable(propertyName)
      用于检查给定的属性是否可以用 for-in 语句进行枚举。
    • toLocaleString()
      返回对象的字符串表示,该字符串与执行环境的地区对应。
    • toString()
      返回对象的字符串表示。
    • valueOf()
      返回对象的字符串、数值、布尔值表示。通常与toString()方法的返回值相同。
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ncTTVVzi-1596703084652)(images/screenshot_1595228465109.png)]

    拓展:声明对象的几种方式

     <script>
    //内置函数
            var obj1=new Object();
            obj1.name="zhuyu";
            obj1.age=21;
            obj1.uni=function(){
                console.log(this.name+this.age);
            }
            console.log(obj1);
            obj1.uni();
     
    //字面量
            var obj2={
                name:"zhuyu2",
                age:21,
                uni2:function(){
                    console.log(this.name+this.age)
                }
            }
            console.log(obj2);
            obj2.uni2();
     
     
    // 自定义函数
            function Obj(name,age){
                this.name=name;
                this.age=age;
                this.uni3=function(){
                    console.log(this.name+this.age)
                }
            }
            var obj3=new Obj("zhuyu3",21);
            console.log(obj3);
            obj3.uni3();
     
    // Es6类
     
            class Obj2{
                constructor(name,age){
                    this.name=name;
                    this.age=age;
                }
                uni4(){
            console.log(this.name+this.age)
                }
            }
            var obj4=new Obj2("zhuyu4",21);
            console.log(obj4);
            obj4.uni4();
     
    //使用Object.create()
     
            var person={
                image:"true",
                uni5:function(){
                    console.log(`名字是${this.name},年龄是${this.age}`);
                }
            }
           var obj5=Object.create(person);
           obj5.name="zhuyu5";
           obj5.age=21;
           obj5.image=false;
           obj5.uni5();
           console.log(obj5)
        </script>
    

    3、基本数据类型和引用数据类型的区别

    总结基本数据类型和引用数据类型区别

    1、声明变量时内存分配不同

    *原始类型:在栈中,因为占据空间是固定的,可以将他们存在较小的内存中-栈中,这样便于迅速查询变量的值
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MXBAdxMe-1596703084654)(images/screenshot_1595226759783.png)]

    *引用类型:存在堆中,栈中存储的变量,只是用来查找堆中的引用地址。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UsfodwlF-1596703084656)(images/screenshot_1595226765978.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1467GOxa-1596703084658)(images/screenshot_1595227087417.png)]

    这是因为:引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响

    2、不同的内存分配带来不同的访问机制

    在javascript中是不允许直接访问保存在堆内存中的对象的,所以在访问一个对象时,首先得到的是这个对象在堆内存中的地址,然后再按照这个地址去获得这个对象中的值,这就是传说中的按引用访问。而原始类型的值则是可以直接访问到的。

    3、复制变量时的不同

    1)原始值:在将一个保存着原始值的变量复制给另一个变量时,会将原始值的副本赋值给新变量,此后这两个变量是完全独立的,他们只是拥有相同的value而已。

    2)引用值:在将一个保存着对象内存地址的变量复制给另一个变量时,会把这个内存地址赋值给新变量,也就是说这两个变量都指向了堆内存中的同一个对象,他们中任何一个作出的改变都会反映在另一个身上。(这里要理解的一点就是,复制对象时并不会在堆内存中新生成一个一模一样的对象,只是多了一个保存指向这个对象指针的变量罢了)。多了一个指针
    浅拷贝:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SoeUg10W-1596703084659)(images/screenshot_1595226833036.png)]

    深拷贝:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JxsVP0Bi-1596703084660)(images/screenshot_1595226846817.png)]

    4、参数传递的不同(把实参复制给形参的过程

    首先我们应该明确一点:ECMAScript中所有函数的参数都是按值来传递的。

    但是为什么涉及到原始类型与引用类型的值时仍然有区别呢?还不就是因为内存分配时的差别。

    1)原始值:只是把变量里的值传递给参数,之后参数和这个变量互不影响。

    2)引用值:对象变量它里面的值是这个对象在堆内存中的内存地址,这一点你要时刻铭记在心!

    因此它传递的值也就是这个内存地址,这也就是为什么函数内部对这个参数的修改会体现在外部的原因了,因为它们都指向同一个对象。

    4、检测类型

    • 法一:typeof

    最基本的判断方式,该操作符返回一个表示数据类型的字符串,numberstringbooleanobjectfunctionundefinedsymbol

    1. ‘undefined’ --未定义的变量或值

    2. ‘boolean’ --布尔类型的变量或值

    3. ‘string’ --字符串类型的变量或值

    4. ‘number’ --数字类型的变量或值

    5. ‘object’ --对象类型的变量或值,或者null(这个是js历史遗留问题,将null作为object类型处理)

    6. ‘function’ --函数类型的变量或值

         console.log(typeof a);    //'undefined'
    
        console.log(typeof(true));  //'boolean'
    
        console.log(typeof '123');  //'string'
    
        console.log(typeof 123);   //'number'
    
        console.log(typeof NaN);   //'number'
    
        console.log(typeof null);  //'object'    
    
        var obj = new String();
    
        console.log(typeof(obj));    //'object'
    
        var  fn = function(){};
    
        console.log(typeof(fn));  //'function'
    
        console.log(typeof(class c{}));  //'function'
    
    • 法二:instanceof

    运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性
    语法:object instanceof constructor
    (1)基础类型

    let num = 1
    num instanceof Number // false
    
    num = new Number(1)
    num instanceof Number // true
    

    这两个都是一样的数值,为什么不一样?
    因为instanceof 检测的是:检测目标的__proto__与构造函数prototype,相同返回true,不同返回false,对于stringboolean是一样的
    注意:

    new String(1) // String {"1"}
    String(1) // "1"
    

    (2) 继承关系的用法

    // 判断 foo 是否是 Foo 类的实例 , 并且是否是其父类型的实例
     function Aoo(){}
     function Foo(){}
     Foo.prototype = new Aoo();//JavaScript 原型继承
    
     var foo = new Foo();
     console.log(foo instanceof Foo)//true
     console.log(foo instanceof Aoo)//true
    

    (3) 复杂类型

    let arr = []
    arr instanceof Array // true
    arr instanceof Object // true
    Array.isArray(arr) // true
    

    注意:
    (new Number(1)) instanceof Object // true
    (4) 其他类型

    let reg = new RegExp(//)
    reg instanceof RegExp // true
    reg instanceof Object // true
    
    let date = new Date()
    date instanceof Date // true
    date instanceof Object // true
    

    但是Fuction不一样

    function A() {}
    let a = new A()
    a instanceof Function // false
    a instanceof Object // true
    A instanceof Function // true
    

    –>分析a为什么不是?

    • a是new出来,所以是经过构造,因此已经是对象,不再是函数,所以false。
    • a是经过构造的对象,返回ture没问题。
    • 如上所述,A是个函数,因此没什么概念上的问题。但是要知道A.__proto__Function.prototypeƒ () { [native code] },这是与object以后处于原型链上层的存在,而且与object平级:
    let obj = {}
    obj.__proto__ // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
    obj.__proto__.prototype // undefined
    
    let A = function() {}
    A.__proto__ // ƒ () { [native code] }
    A.__proto__.prototype // undefined
    

    本章内容图谱:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qZLYd9tI-1596703084662)(images/screenshot_1595231058140.png)]

    展开全文
  • Java基本类型变量与引用类型变量

    千次阅读 2015-04-22 17:13:59
    Java基本类型变量与引用类型变量  (2013-04-08 20:47:54) 转载▼ 标签:  java   设计模式   java设计模式   语句   it 分类: Java Java基本类型变量...
  • JavaScript原始类型和引用类型

    千次阅读 2015-10-23 09:43:58
    引子理解和使用对象是理解整个 JavaScript 的关键JavaScript 把对象作为语言的中心,几乎所有的 JavaScript 的...JavaScript 中的数据分为两种类型:原始类型和引用类型,两者都通过对象进行访问。原始类型保存为简单
  • 2011新版JAVA题解JAVA简单数据类型 2011新版JAVA题解JAVA简单数据类型1下列哪一个是合法的标识符 A12class; B+viod; C-5; D_black; 答D 2下列哪一个不是JAVA语言中保留字 Aif; Bsizeof; Corivate; Dnull; 答B 3下列...
  • Java中的数据类型分为两大类,基本数据类型引用数据类型 一、数据类型二、基本数据类型引用数据类型的区别
  • 我们都知道,c#的两大数据类型分别为值类型(int,float,double,char,DataTime)和引用类型(类、托管、数组和接口)。很多人或许闭着眼睛都能说出值类型包括简单类型、...为此,了解值类型和引用类型的本质差异就变的很
  • 你不知道的JavaScript–Item4 基本类型和基本包装类型(引用类型) 标签: <a h
  • 创建一个严格类型VI引用,即带严格类型定义的引用,使用通过引用调用节点或开始异步调用节点动态调用VI。严格类型的VI引用句柄不仅指向被调用的VI,还指定了VI的连线...按照下列步骤,为“通过引用调用”节点或“开...
  • 引用类型Object(类型)

    千次阅读 2018-10-16 20:52:50
    Object是所有类型的基础类,Object也是ECMAScript中使用最多的一个类型,虽然Object实力不具备多少功能,但是用于存储和传输数据非常有用。 Object实力的创建方式: (1)new操作符后面跟Object构造函 var person...
  • java中四种引用类型

    千次阅读 2015-08-27 11:42:50
    今天看代码,里面一个类java.lang.ref.SoftReference把小弟弄神了,试想一下,接触java已经3年了哇,连lang包下面的类都不了解,怎么混。后来在网上查资料,感觉收获颇多,现记录如下。对象的强、软、弱和虚引用...
  • JAVA基础 引用类型变量和基本类型变量

    千次阅读 多人点赞 2016-08-16 22:32:27
    引用类型变量的名字是复杂数据的存储地点。包括类 数组(字符串) 接口 String s="Hello world!"; 许多人都写过这样的语句,但是,我们到底声明了什么呢?一休,你回答一下。 一休:声明了一个String对象,...
  • C++引用类型

    千次阅读 2011-06-16 18:53:00
    先看一个简单的程序,说明引用的基本知识view plaincopy to clipboardprint?#include #include using namespace std; int main(void) { int* p; int a=5; int b=6; int& c=a;//b是a的引用,当定义b这个引用
  • 结构体与类(验证结构体是值类型,类是引用类型)
  • Java面试题19下面有关java的引用类型,说法正确的? A:对于一个对象来说,只要强引用的存在,它就会一直存在于内存中 B:如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾...
  • C# 哪些是引用类型??

    千次阅读 2019-10-09 09:33:01
    引用类型:类class,对象object,数组type [],字符串 string,委托delegate,接口interface 值类型:整数int,浮点数float double,布尔bool,字符char,结构struct,枚举enum ...
  • Java的引用数据类型--枚举

    千次阅读 2020-03-30 17:17:58
    标题:Java的引用数据类型--枚举 作者:Nstar 时间:2020年3月30日 * */ public enum Name_test { //枚举数据类型,Java中的引用数据类型 ZHANGSAN,LISI,WANGWU,ZHAOLIU } class Test_name{ public static void ...
  • C#中对象类型主要两种——引用类型(重量级对象)和值类型(轻量级对象)。  引用类型总是在堆中分配(除非使用 stackalloc 关键字),并给予一个额外的间接层;也即,它们需要通过对其存储位置的引用来访问。...
  • 讲解:值类型,引用类型 ------------------------------------------------------------------------------- 很多同学学过 C 语言,知道 C 语言可以声明指针类型的变量。那就会问, java 中没有指针呢?...
  • 关键字: js一、类型转换 1.转换成字串 ECMAScript的Boolean值、数字和字串的原始值的有趣之处在于它们是伪对象,这意味着它们实际上具有属性和方法。 如: Js代码 var sColor = "blue"; alert(sColor.length);//...
  • C# 指针学习笔记之指针类型

    千次阅读 2014-09-05 09:31:11
    大学的时候学过C++、C,最近工作也不是很...在不安全的上下文中,类型可以是指针类型以及值类型或引用类型。指针类型声明具有下列形式之一: type* identifier; void* identifier; //allowed but not recomm
  • JavaScript:数据类型

    千次阅读 2016-05-31 15:21:36
     ECMAScript中五种简单的数据类型(也称为基本数据类型):Undefined、Null、Boolean、Number、String。还有一种复杂数据类型—Object,Object本质上是由一组无序的名值对组成的。 1、typeof  鉴于ECMAScript是...
  • C#数据类型

    千次阅读 多人点赞 2012-04-23 19:11:16
    C#的数据类型可以分为3类:数值类型,引用类型,指针类型。指针类型仅在不安全代码中使用。  值类型包括简单类型(如字符型,浮点型和...对于引用类型的变量,完全可能让两个不同的变量引用同一个对象,这样一来,
  • 基本数据类型和封装类型的区别

    千次阅读 多人点赞 2019-09-23 13:27:34
    基本数据类型和封装类型的区别基本数据类型基本数据类型介绍封装数据类型基本数据类型对应的封装类介绍基本数据类型与封装类对应关系对照表String类他们的区别总结合理的创建标题,助于目录的生成如何改变文本的...
  • 模板非类型形参的详细阐述

    千次阅读 2016-03-02 15:34:58
    关于模板的非类型形参,网上很多内容,C++primer只有大概一页的阐述,但是都不够清晰详细。下面我尽可能从自己的角度去给大家描述一下非类型形参的相关细节。如果想进一步理解非类型形参以及模板内容可以阅读C++...
  • // 使用完了a,将它设置为soft 引用类型,并且释放强引用; SoftReference sr = new SoftReference(a); a = null; … // 下次使用时 if (sr!=null) {  a = sr.get(); } else{  ...
  • 编译错误 说明: 在编译向该请求提供服务所需资源的过程中出现错误。请检查下列特定错误详细... 编译器错误消息: CS0246: 找不到类型或命名空间名称“FrameWork”(是否缺少 using 指令或程序集引用?)解决方案:添加引用

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 135,471
精华内容 54,188
关键字:

下列类型属于引用类型的有