zoukankan      html  css  js  c++  java
  • java深入浅出之数据结构

    1.整形数据

    byte、short、int、long,分别是1248个字节的存储量,取值范围也是依次增大的,其中int是正负21亿多;

    long a = 1111222233334444L;记住后面要加上L,否则因为1111222233334444为int型,且超出int范围;

    在这里插入一个面试题:

    short c = 1;c = c +1;对错与否?

    因为定义c为short型,c+1计算结果是int型(short型的c与int型的1相加,结果为int型),所以要强转

    c=(short)c+1;

    然而,对于c+=1;这个算法和(c=c+1)效果一毛一样,但是,却存在一个隐式的类型转换;

    c+=1实际上是c=(short)c+1;

    所以对于上面提到的:c=c+1错;c+=1对。

    那么问题来了,在上面自增运算中我们可以看到,int型数据赋值给short型变量时,要进行类型强转,

    按照long a = 1111222233334444L定义是看到的那样,不加L,数值默认是int型,那么short c = 1,这里

    的1也应当是int型,将int型的1赋值给short型变量却不用强转,

    思索再三,只能给出这样一个解释:在变量初始化赋值的时候,只要赋的值符合变量类型要求(取值范围)

    就不用进行类型强转,在进行变量操作之后再进行赋值时要检查等号两边数据类型。

    2.浮点型数据

    float、double;分别48字节存储量,带小数点float7位小数,double15位,精度更高;

    又是面试题:

    float a =3.4;double b =2.4;

    其实前面的float型变量的赋值时错误的,因为上面的3.4和2.4没有后缀默认是double型的,所以对于

    float a = 3.4,这里初始化时将double型数据赋值给float型变量,虽然3.4满足float型数据范围要求;

    但是还是要强转类型 float a = (float) 3.4;

    这是浮点型数据初始化时与整形数据初始化时的区别。

    3.两数运算返回值类型

    只要有一个是double类型,最终返回double类型;否则,

    只要有一个是float类型,最终返回float类型;否则,

    只要有一个是long类型,最终返回long类型;

    否则,最终返回int型。

    4.Integer包装类

    这里讲一个自动装箱/拆箱的问题:

    自动装箱:

    Integer a = 100;Integer b = 100;a==b?

    这里的自动装箱,Integer a=Integer.valueOf(100);

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    

    我们可以看到,自动装箱实际上是调用了Integer类的valueOf方法,当传进来的值在一个区间[-128,127]时,

    直接返回一个数值;所以上述a和b都是指向同一个数值IntegerCache.cache[100-IntegerCache.low];

    a==b返回true。

    但是当Integer c = 150;Integer d = 150;情况就不一样了;

    Integer c = Integer.valueOf(150)=new Integer(150);同理d也是等于new Integer(150);

    在这里补充一下,就算数值相同,使用new和运算符算出来的结果,都会占据内存中的一块新地址,所以,这里

    c、d虽然数值上相同,但是它们各自指向内存中截然不同的地址,

    所以c==d返回false。

    自动拆箱:

    Integer a = 12;------------自动装箱

    int b = a;--------------------自动拆箱

    我们来看看Integer类的源码:

    public final class Integer extends Number implements Comparable<Integer> {
        private final int value;
         public Integer(int value) {
                this.value = value;
                }
         public int intValue() {
                return value;
                }
    }

    不难发现,自动装箱的时候实现了Integer a = new Integer(12);而自动拆箱的时候实际上是执行了intValue()方法;

    int b = a.intVaule();

    5.值/引用传递以及堆栈的概念

    java开辟了两类存储区域---栈、堆:

    栈:基本类型的变量(8大基本数据类型,int b 这里的b)以及对象的引用变量(String a 这里的a);

    堆:new等指令创建的对象和数组(new A()---一个A类的实例化对象);

    在性能上,栈的存取速度要比堆快,仅次于寄存器,并且由于栈数据的可共享性,我们看到的几乎都是对栈数据

    进行操作,很少见到人对new出来的对象进行直接操作。

    A a = new A();

    这句代码应作如下解析:

    1.调用A类的无参构造器来实例化一个A的对象new A();

    2.在堆内存中开辟一块空间存放上面实例化的A对象new A();

    3.在栈内存中开辟一块空间给A类的引用变量a,此时a=null;

    4.最后将对象new A()在堆内存中的地址传递给引用变量a。

    这就是A类的引用(变量)指向A类的(实例化)对象。

    引用传递:传递的变量指向内存中的地址,所以对该变量操作的结果会直接影响内存中的数据;

    值传递:传递变量的是值的拷贝,所以操作的对象也只是源值的副本,所以源值是不会发生改变的。

    下面来通过一个小程序来检测一下值/引用传递:

    package com.eco.factory;
    
    public class Test {
    
        /**
         * 检测不同类型参数的传值方式--基本类型变量和引用变量
         */
        // 引用变量作为参数,对数据修改
        public void change(A a) {
            a.age = 20;
        }
    
        // 基本类型变量作为参数,对数据修改
        public void change(int b) {
            b = 20;
        }
    
        public static void main(String[] args) {
            Test test = new Test();
            int a = 5;
            int b = a;
            System.out.println("int型数据处理之前:" + a);
            test.change(a);
            System.out.println("int型数据处理之后:" + a);
            if (b == a) {
                System.out.println("数据处理前后没发生变化,所以基本类型参数是值传递");
            } else
                System.out.println("数据处理前后发生变化,所以基本类型参数是引用传递");
            A c = new A();
            c.age = 5;
            System.out.println("A型数据处理之前:" + c.age);
            int d = c.age;
            test.change(c);
            System.out.println("A型数据处理之后:" + c.age);
            if (d == c.age) {
                System.out.println("数据处理前后没发生变化,所以引用变量型参数是值传递");
            } else
                System.out.println("数据处理前后发生变化,所以引用变量型参数是引用传递");
        }
    
    }
    
    class A {
        public int age;
    }

    控制台打印输出:

    int型数据处理之前:5
    int型数据处理之后:5
    数据处理前后没发生变化,所以基本类型参数是值传递
    A型数据处理之前:5
    A型数据处理之后:20
    数据处理前后发生变化,所以引用变量型参数是引用传递

    注:

    在《Java核心技术》这本书中第四章第5节提到,java只有值传递,并且用了一个swap来验证这一论

    点,在我看来是有待商榷的,我们先来看看这个swap方法:

    package com.eco.factory;
    
    public class Test {
        public void swap(A x, A y) {
            A temp = x;
            x = y;
            y = temp;
        }
    
        public static void main(String[] args) {
            Test test = new Test();
            A a = new A("Jack");
            A b = new A("Tom");
            test.swap(a, b);
            System.out.println("***********************************");
            System.out.println("a的姓名:" + a.name);
            System.out.println("b的姓名:" + b.name);
        }
    }
    
    class A {
        public int age;
        public String name;
        public A(String name) {
            this.name = name;
        }
    }

    该书提出,如果java对对象采用的是引用调用(传递),那么这个swap方法就能实现数据交换的结果,

    结果就该显示a的姓名是Tom,b的姓名是Jack,实际结果a 姓名依然是Jack,b依然是Tom,所以作者

    得出结论:java对对象采用的不是引用传递。

    那么现在,我们来对这个程序稍作修改:

    package com.eco.factory;
    
    public class Test {
    
        public void swap(A x, A y) {
            A temp = x;
            x = y;
            y = temp;
            System.out.println("a的姓名:" + x.name);
            System.out.println("b的姓名:" + y.name);
        }
    
        public static void main(String[] args) {
            Test test = new Test();
            A a = new A("Jack");
            A b = new A("Tom");
            test.swap(a, b);
            System.out.println("***********************************");
            System.out.println("a的姓名:" + a.name);
            System.out.println("b的姓名:" + b.name);
        }
    }
    
    class A {
        public int age;
        public String name;
    
        public A(String name) {
            // super();
            this.name = name;
        }
    }

    控制台打印:

    a的姓名:Tom
    b的姓名:Jack
    ***********************************
    a的姓名:Jack
    b的姓名:Tom

    我们可以看到在swap方法内部实际上是真的实现了作者所要求的数据交换的,这一点我认为可以反驳

    作者的数据没有进行交换的论点,但是到了主方法上,a的姓名又回来了,我认为这是因为在swap方法

    内部定义的x,y由于是局部变量,在方法结束之后这两个变量是会被销毁的。而主方法所访问的是a、b

    这两个引用变量,而非x、y这两个引用变量(因为在全局作用域也没用定义过,所以实际上也是访问不

    了的)

    所以我认为,java对对象采用的是引用传递,对象并不能够反映一个数据的本质,而swap方法实现了对

    a对象的复制---x,方法体内部也可以看到自始至终是对这个克隆体x的各种操作,丝毫没有涉及到a对象,

    这就和基本数据类型的值传递一样,自始至终都是对克隆体的操作。

    值传递:以源数据的复制品为操作对象,不会改变原数据;

    引用传递:以引用变量的复制品为操作对象,不会改变原引用变量,但会同步改变两个引用变量共同指向

                     的数据。

    6.getter方法的返回值类型问题

    我们通常在写一个类的getter、setter方法时都会直接用IDE工具提供的一键生成getter、setter方法,但是

    当返回值类型不是基本数据类型的话,会产生一些问题:

    package com.eco.factory;
    
    import java.util.Date;
    
    public class Test2 {
        public static void main(String[] args) {
            Date date = new Date();
            Father f = new Father("桔子桑", date);
            System.out.println(f.getHirday());
            f.getHirday().setHours(16);
            System.out.println(f.getHirday());
        }
    }
    
    class Father {
        private String name;
        private Date hirday;
    
        public Father(String name, Date hirday) {
            this.name = name;
            this.hirday = hirday;
        }
    
        public String getName() {
            return name;
        }
    
        public Date getHirday() {
            return hirday;
        }
    
    }

    控制台打印输出:

    Mon Dec 25 00:24:33 CST 2017
    Mon Dec 25 16:24:33 CST 2017

    我们可以看到,就算Father类的成员变量hirday是private声明,还是可以不通过setter方法来改变hirday的

    值,在Java核心技术这本书中提供了一个方法:

    package com.eco.factory;
    
    import java.util.Date;
    
    public class Test2 {
        public static void main(String[] args) {
            Date date = new Date();
            Father f = new Father("桔子桑", date);
            System.out.println(f.getHirday());
            Date obj  =(Date)f.getHirday();
            obj.setHours(17);
            System.out.println(f.getHirday());
        }
    }
    
    class Father {
        private String name;
        private Date hirday;
    
        public Father(String name, Date hirday) {
            this.name = name;
            this.hirday = hirday;
        }
    
        public String getName() {
            return name;
        }
    
        public Object getHirday() {
            return hirday.clone();
        }
    
    }

    控制台:

    Mon Dec 25 00:34:16 CST 2017
    Mon Dec 25 00:34:16 CST 2017

    可以看到,当返回的是hirday.clone()的时候,就算对返回值如何操作,都不会对实例化对象的hirday值

    作任何改变,因为红字操作的始终只是hirday的副本,对真实值丝毫没影响。

    7.

  • 相关阅读:
    最详细的hadoop2.2.0集群的HA高可靠的最简单配置
    HIVE 在执行大量数据JOIN的时候,容易产生内存不足的情况
    机器学习的学习笔记1
    AngularJS在IE8的支持
    OC 异常处理
    OC NSString练习
    OC继承
    OC--@property和@synthesize关键字
    OC输入输出
    OC数组和字典简单使用
  • 原文地址:https://www.cnblogs.com/eco-just/p/8071755.html
Copyright © 2011-2022 走看看