基本类型封装类
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是一个序列化的对象或数组。
- 六个构造字符:
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 ; , 逗号 - 在这六个构造字符的前或后允许存在无意义的空白符(ws):
ws = *(%x20 /; 空间
%x09 /; 水平标签
%x0A /; 换行或换行
%x0D); 回程 - 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
随想
如果一样工具他有自己独特的地方,便利的地方,同时它也能做一些很常见的事,其实那些常见的事其他东西也能实现,重要的是怎么把每一样东西独特的地方发挥到极致,并把这些在不同部分发挥极致的工具组合在一起。