Java学习笔记(二)

基本类型封装类

Java中一切都是对象。所有的函数,变量,常量都必须在一个类里定义。这本来很好,很完美的规定。但是Java在一开始设计的时候,出于性能上的考虑,却留下了一道口子:primitive类型,我们在开发中也常称为值类型,或者干脆按字面意思翻译为基础类型或者原始类型。

primitive类型中包括:byte int char short int long float double boolen
为了满足强对象,将每个都封装在一个由Object类继承来的类中,又引入了它们的包装类(wrapper class),对应如下:

byte -> Byte
char -> Character
short -> Short
int -> Integer
long -> Long
float -> Float
double -> Double
boolean -> Boolean

自动拆装箱
Java中有两种表示:值和对象。为了让你用的顺手,就弄了这么一个功能:自动拆装箱。
装箱:值-〉对象
拆箱:对象-〉值

//需要用对象的时候允许你传一个值
public class Test {
    public static void main(String args[]) {
        Integer a = 1;
        Integer b = new Integer(1);

        Integer c = inc(0);
    }   
//需要一个值的时候允许你传一个对象
    public static int inc(Integer x) {
        return x + 1;
    }   
  
    public class Test {
    public static void main(String args[]) {
        int a = new Integer(1);
        int b = Integer.valueOf(1);
        int c = inc(0);
    }   

    public static Integer inc(Integer x) {
        return x + 1;
    }   
}

但是就出现了很神奇的结果!!!

public class Test {
    public static void main(String args[]) {
        Integer a = new Integer(1);//这样写不行了,不支持Integer()了,但是吧,也能运行
        Integer b = Integer.valueOf(1);
        Integer c = inc(0);
        Integer d = 1;
        System.out.println(a == b);  // false
        System.out.println(a.equals(b)); // true
 
        System.out.println(a == c); // false 
        System.out.println(a.equals(c)); // true

        System.out.println(a == d);  // false 
        System.out.println(a.equals(d)); // true

        System.out.println(b == c);  // true
        System.out.println(b.equals(c));  //true

        System.out.println(b == d);  // true
        System.out.println(b.equals(d)); // true

        /*System.out.println(((Long)1L) == 1); // true
        System.out.println(new Long(1).equals(1)); // false*/
        //这里这种写法已经不行了,现在不允许使用long()

        Long e =(long)100 ;//Long e = 100;
        Long f =(long)100; //Long f = 100;
        System.out.println(e == f);  // true
        e = (long)1000;
        f = (long)1000;
        System.out.println(e == f);  // false
    }

    public static Integer inc(Integer x) {
        return x + 1;
    }
}

众多出人意料的结果,这就是wrapper类存在的众多问题的部分,除了在使用容器,泛型的时候使用外包类,否则尽量少使用。
⚠️"=="判断的是两边的对象是不是同一个对象,即两个引用指向同一个对象。"equals"是函数,如何实现取决于类里如何定义。

JDK中源码解释问题:

class Long {
    public Long(long value) {
        this.value = value;
    }

    private static class LongCache {
        private LongCache(){}

        static final Long cache[] = new Long[-(-128) + 127 + 1];

        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Long(i - 128);
        }
    }
//-128~127区间内
    public static Long valueOf(long l) { 
        final int offset = 128;
        if (l >= -128 && l <= 127) { // will cache
            return LongCache.cache[(int)l + offset];
        }
        return new Long(l);
    }

    // 神坑!!!!
    public boolean equals(Object obj) {
        if (obj instanceof Long) {
            return value == ((Long)obj).longValue();
        }   
        return false;
    }
}

直接使用new构造一个长整型的时候,会生成一个新的对象。而使用 valueOf 的时候,如果参数位于[-128, 127]这个区间内,则直接从cache数组中取一个对象,而不是新建,所以100的时候是同一个对象,但是1000的时候就重建了。
自动装箱时,Java调用了valueOf函数,拆箱时,调用 xxxValue 函数。这就回答了,为什么b, c, d 是同一个对象,而与a却是不同的对象。
下面这个比较难理解:

        System.out.println(((Long)1L) == 1); // true
        System.out.println(new Long(1).equals(1)); // false

“==”操作符的左边是一个Long型的对象,为了比较就使用了自动拆箱,左边拆箱成了整数1,所以与右边的值是相等的。而第二行,equals参数接受一个Object对象,所以1就被自动装箱成了Integer.valueOf(1),看上面的long的源码中equals部分如果不是Long型的直接返回false

思考下面的输出?

        System.out.println(((Long)1000L) == 1000);//true
        System.out.println(((Long)1000L) == Long.valueOf(1000));//false
        System.out.println(new Long(1).equals(1L));//true

第一行实际上是Long对象拆箱为1000,两边值相等,所以返回true;
第二行就是右边1000超过了-128~127的范围,又创建了一个新的对象,所以两个指针不同;
第三行是在括号中的1L是返回Long类型,是函数equals接受的类型所以可以返回true;如果是1,就是false

字面量都不是对象

字面量(literal)是用于表达源代码中一个固定值的记号。

定义看上去不那么直观。其实很简单,整数1,2, 3是整型字面量;用引号引起来的"hello world",是字符串(String)字面量;1.0, 2.1是浮点型字面量;‘a’, 'c’是字符型字面量。可以使用字面量初始化primitive类型,也可以用于初始化某些特定的对象。例如:

public class Test {
    public static void main(String args[]) {
        int a = 1;
        char c = 'z';
        String s = "hello java!";
        double[] t = {1.0, 2.1, 3.4};
    }   
}

Java允许通过字面量给String类型的对象赋值,却不允许字面量本身就是一个对象。对于一个强对象的语言,这其实是很落后的做法。我们来看几个其他语言的例子:

 Python,字符串连接的写法
"-".join["hello", "world"]  # hello-world

 groovy, 打印三次hello world
3.times {
    println("hello world");
}

  scala
"hello".length  //res0: Int = 5
3.compareTo(2)

 深刻改变网络消息格式的JSON: 
 javascript
var d = {"hello" : 1, "world" : 2};

其实JSON的结构不过就是javascript的字典类型的字面量而已。这种数据即代码的做法才是现代语言应该具备的特征。

我们看到,在很多种语言中,都可以把字面量当做一个对象进行调用。例如groovy和ruby里,都可以直接在一个整数上调用times,代表后面的代码块要执行多少次。在这些语言里,才真正的一切都是对象,就连函数都是对象。对比起来,Java不仅字面量不是对象,而且字面量向对象的转变还留下了一个个不大不小的坑。这已经不是bad taste的问题,而是强对象这件事情,Java 根本就没做对。

作业解答:
1、其实Integer里的实现和Long是差不多的,原因也相似。

  public static Integer valueOf(String s, int radix) throws NumberFormatException {
        return Integer.valueOf(parseInt(s,radix));
    }

    /**
     * Returns an {@code Integer} object holding the
     * value of the specified {@code String}. The argument is
     * interpreted as representing a signed decimal integer, exactly
     * as if the argument were given to the {@link
     * #parseInt(java.lang.String)} method. The result is an
     * {@code Integer} object that represents the integer value
     * specified by the string.
     *
     * <p>In other words, this method returns an {@code Integer}
     * object equal to the value of:
     *
     * <blockquote>
     *  {@code new Integer(Integer.parseInt(s))}
     * </blockquote>
     *
     * @param      s   the string to be parsed.
     * @return     an {@code Integer} object holding the value
     *             represented by the string argument.
     * @exception  NumberFormatException  if the string cannot be parsed
     *             as an integer.
     */
    public static Integer valueOf(String s) throws NumberFormatException {
        return Integer.valueOf(parseInt(s, 10));
    }

    /**
     * Cache to support the object identity semantics of autoboxing for values between
     * -128 and 127 (inclusive) as required by JLS.
     *
     * The cache is initialized on first usage.  The size of the cache
     * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
     * During VM initialization, java.lang.Integer.IntegerCache.high property
     * may be set and saved in the private system properties in the
     * sun.misc.VM class.
     */

    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

    /**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  i an {@code int} value.
     * @return an {@code Integer} instance representing {@code i}.
     * @since  1.5
     */
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

    /**
     * The value of the {@code Integer}.
     *
     * @serial
     */
    private final int value;

    /**
     * Constructs a newly allocated {@code Integer} object that
     * represents the specified {@code int} value.
     *
     * @param   value   the value to be represented by the
     *                  {@code Integer} object.
     */
    public Integer(int value) {
        this.value = value;
    }

    /**
     * Constructs a newly allocated {@code Integer} object that
     * represents the {@code int} value indicated by the
     * {@code String} parameter. The string is converted to an
     * {@code int} value in exactly the manner used by the
     * {@code parseInt} method for radix 10.
     *
     * @param      s   the {@code String} to be converted to an
     *                 {@code Integer}.
     * @exception  NumberFormatException  if the {@code String} does not
     *               contain a parsable integer.
     * @see        java.lang.Integer#parseInt(java.lang.String, int)
     */
    public Integer(String s) throws NumberFormatException {
        this.value = parseInt(s, 10);
    }

2、json格式的相关内容
JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

json语法规则

JSON 语法规则

JSON是一个标记符的序列。这套标记符包含六个构造字符、字符串、数字和三个字面名。
JSON是一个序列化的对象或数组。

  1. 六个构造字符:
    begin-array = ws %x5B ws ; [ 左方括号
    begin-object = ws %x7B ws ; { 左大括号
    end-array = ws %x5D ws ; ] 右方括号
    end-object = ws %x7D ws ; } 右大括号
    name-separator = ws %x3A ws ; : 冒号
    value-separator = ws %x2C ws ; , 逗号
  2. 在这六个构造字符的前或后允许存在无意义的空白符(ws):
    ws = *(%x20 /; 空间
    %x09 /; 水平标签
    %x0A /; 换行或换行
    %x0D); 回程
  3. JSON的值:
    3.1 JSON的构成: ws 值 ws [1]
    3.2 值可以是对象、数组、数字、字符串或者三个字面值(false、null、true)中的一个。值中的字面值中的英文必须使用小写。
    3.2.1 对象由花括号括起来的逗号分割的成员构成,成员是字符串键和上文所述的值由逗号分割的键值对组成,如:
    {"name": "John Doe", "age": 18, "address": {"country" : "china", "zip-code": "10000"}}

3.2.2数组是由方括号括起来的一组值构成,如:

[3, 1, 4, 1, 5, 9, 2, 6]

3.2.3 字符串与C或者Java的字符串非常相似。字符串是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。
3.2.4数字也与C或者Java的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编码细节。 [2]
一些合法的JSON的实例:

{"a": 1, "b": [1, 2, 3]}
[1, 2, "3", {"a": 4}]
3.14
"plain_text"

JSON 与 JS 对象的关系
很多人搞不清楚 JSON 和 JS 对象的关系,甚至连谁是谁都不清楚。其实,可以这么理解:
JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。

var obj = {a: 'Hello', b: 'World'}; //这是一个对象,注意键名也是可以使用引号包裹的
var json = '{"a": "Hello", "b": "World"}'; //这是一个 JSON 字符串,本质是一个字符串

JSON 和 JS 对象互转
要实现从JSON字符串转换为JS对象,使用 JSON.parse() 方法:

var obj = JSON.parse('{"a": "Hello", "b": "World"}'); //结果是 {a: 'Hello', b: 'World'}

要实现从JS对象转换为JSON字符串,使用 JSON.stringify() 方法:

var json = JSON.stringify({a: 'Hello', b: 'World'}); //结果是 '{"a": "Hello", "b": "World"}'

常用类型

任何支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。但是对象和数组是比较特殊且常用的两种类型。
对象:对象在 JS 中是使用花括号包裹 {} 起来的内容,数据结构为 {key1:value1, key2:value2, …} 的键值对结构。在面向对象的语言中,key 为对象的属性,value 为对应的值。键名可以使用整数和字符串来表示。值的类型可以是任意类型。

对象是一个无序的“‘名称/值’对”集合。一个对象以{左括号开始,}右括号结束。每个“名称”后跟一个:冒号;“‘名称/值’ 对”之间使用,逗号分隔。

{"firstName": "Brett", "lastName": "McLaughlin"}                                        

数组:数组在 JS 中是方括号 [] 包裹起来的内容,数据结构为 [“java”, “javascript”, “vb”, …] 的索引结构。在 JS 中,数组是一种比较特殊的数据类型,它也可以像对象那样使用键值对,但还是索引使用得多。同样,值的类型可以是任意类型

和普通的 JS 数组一样,JSON 表示数组的方式也是使用方括号 []{ 
      "people":
     [ 
        {"firstName": "Brett", "lastName":"McLaughlin" },                                 {"firstName":"Jason","lastName":"Hunter"}
     ]
}
这不难理解。在这个示例中,只有一个名为 people的变量,值是包含两个条目的数组,每个条目是一个人的记录,其中包含名和姓。上面的示例演示如何用括号将记录组合成一个值。当然,可以使用相同的语法表示更过多的值(每个值包含多个记录)。
在处理 JSON 格式的数据时,没有需要遵守的预定义的约束。所以,在同样的数据结构中,可以改变表示数据的方式,也可以使用不同方式表示同一事物。

⚠️简单地说,JSON 可以将 JavaScript 对象中表示的一组数据转换为字符串,然后就可以在网络或者程序之间轻松地传递这个字符串,并在需要的时候将它还原为各编程语言所支持的数据格式,例如在 PHP 中,可以将 JSON 还原为数组或者一个基本对象。在用到AJAX时,如果需要用到数组传值,这时就需要用JSON将数组转化为字符串。

3、修改Long.java,看看能不能改变equals的行为,使得下面的语句输出为true
System.out.println(new Long(1).equals(1));
判断一下传入参数是否合适,然后if去掉将其换成将传入的参数转化成Long类型,然后用值比较返回true/false

随想

如果一样工具他有自己独特的地方,便利的地方,同时它也能做一些很常见的事,其实那些常见的事其他东西也能实现,重要的是怎么把每一样东西独特的地方发挥到极致,并把这些在不同部分发挥极致的工具组合在一起。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值