赞
踩
Java 中有 8 种基本数据类型,分别为:
byte
、short
、int
、long
float
、double
char
boolean
。这 8 种基本数据类型的默认值以及所占空间的大小如下:
基本类型 | 位数 | 字节 | 默认值 | 取值范围 |
---|---|---|---|---|
byte | 8 | 1 | 0 | -128 ~ 127 |
short | 16 | 2 | 0 | -32768(-2^15) ~ 32767(2^15 - 1) |
int | 32 | 4 | 0 | -2147483648 ~ 2147483647 |
long | 64 | 8 | 0L | -9223372036854775808(-2^63) ~ 9223372036854775807(2^63 -1) |
float | 32 | 4 | 0f | 1.4E-45 ~ 3.4028235E38 |
double | 64 | 8 | 0d | 4.9E-324 ~ 1.7976931348623157E308 |
char | 16 | 2 | ‘u0000’ | 0 ~ 65535(2^16 - 1) |
boolean | 1 | false | true、false |
static
修饰 )存放在 Java 虚拟机的堆中。null
null
。==
比较的是值。==
比较的是对象的内存地址。所有整型包装类对象之间值的比较,全部使用 equals()
方法。public class Test {
// 成员变量,存放在堆中
int a = 10;
// 被 static 修饰,也存放在堆中,但属于类,不属于对象
// JDK1.7 静态变量从永久代移动了 Java 堆中
static int b = 20;
public void method() {
// 局部变量,存放在栈中
int c = 30;
static int d = 40; // 编译错误,不能在方法中使用 static 修饰局部变量
}
}
Byte
,Short
,Integer
,Long
这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据Character
创建了数值在 [0,127] 范围的缓存数据Boolean
直接返回 True
or False
。Integer i1 = 33;
Integer i2 = 33;
System.out.println(i1 == i2);// 输出 true
Float i11 = 333f;
Float i22 = 333f;
System.out.println(i11 == i22);// 输出 false
Double i3 = 1.2;
Double i4 = 1.2;
System.out.println(i3 == i4);// 输出 false
Integer i1 = 40;
Integer i2 = new Integer(40);
System.out.println(i1==i2);
//Integer i1=40 这一行代码会发生装箱,也就是说这行代码等价于 Integer i1=Integer.valueOf(40) 。因此,i1 直接使用的是缓存中的对象。而Integer i2 = new Integer(40) 会直接创建新的对象
//结果为fasle
Integer i = 10; //装箱
int n = i; //拆箱
L1 LINENUMBER 8 L1 ALOAD 0 BIPUSH 10 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; PUTFIELD AutoBoxTest.i : Ljava/lang/Integer; L2 LINENUMBER 9 L2 ALOAD 0 ALOAD 0 GETFIELD AutoBoxTest.i : Ljava/lang/Integer; INVOKEVIRTUAL java/lang/Integer.intValue ()I PUTFIELD AutoBoxTest.n : I RETURN
valueOf()
==方法xxxValue()
方法。
Integer i = 10
等价于Integer i = Integer.valueOf(10)
int n = i
等价于int n = i.intValue()
;
语法形式:
public
,private
,static
等修饰符所修饰static
所修饰;final
所修饰。存储方式:从变量在内存中的存储方式来看
static
修饰的,那么这个成员变量是属于类的static
修饰,这个成员变量是属于实例的。生存时间:从变量在内存中的生存时间上看
默认值:从变量是否有默认值来看
final
修饰的成员变量也必须显式地赋值)static
关键字修饰的变量。它可以被类的所有实例共享,无论一个类创建了多少个对象,它们都共享同一份静态变量。StaticVariableExample.staticVar
(如果被 private
关键字修饰就无法这样访问了)。public class StaticVariableExample {
// 静态变量
public static int staticVar = 0;
}
通常情况下,静态变量会被 final
关键字修饰成为常量。
public class ConstantVariableExample {
// 常量
public static final int constantVar = 0;
}
封装、继承、多态
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能
核心点:
多态,顾名思义,表示一个对象具有多种的状态,具体表现为父类的引用指向子类的实例。
多态的特点:
default
关键字在接口中定义默认方法)单继承、多实现
成员变量修饰符不同、赋值不同:
用途不同:
举例说明浅拷贝,实现Cloneable接口,并重写clone()方法,直接调用父类Obejct的clone()方法
public class Address implements Cloneable{ private String name; // 省略构造函数、Getter&Setter方法 @Override public Address clone() { try { return (Address) super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(); } } } public class Person implements Cloneable { private Address address; // 省略构造函数、Getter&Setter方法 @Override public Person clone() { try { Person person = (Person) super.clone(); return person; } catch (CloneNotSupportedException e) { throw new AssertionError(); } } }
Person person1 = new Person(new Address("武汉"));
Person person1Copy = person1.clone();
// true
System.out.println(person1.getAddress() == person1Copy.getAddress());
从输出结构就可以看出,
person1
的克隆对象和person1
使用的仍然是同一个Address
对象。
这里我们简单对 Person
类的 clone()
方法进行修改,连带着要把 Person
对象内部的 Address
对象一起复制。
@Override
public Person clone() {
try {
Person person = (Person) super.clone();
person.setAddress(person.getAddress().clone());
return person;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
Person person1 = new Person(new Address("武汉"));
Person person1Copy = person1.clone();
// false
System.out.println(person1.getAddress() == person1Copy.getAddress());
从输出结构就可以看出,显然 person1的克隆对象和 person1
包含的 Address
对象已经是不同的了。
简单来说,引用拷贝就是两个不同的引用指向同一个对象。
以下图来描述浅拷贝、深拷贝、引用拷贝:
==是运算符,equals是方法
若比较的对象是基本数据类型,则比较数值是否相等
若比较的对象是引用数据类型,则比较的是对象的内存地址是否相等
不管是比较基本数据类型,还是引用数据类型的变量,其比较的都是值,只是引用类型变量存的值是对象的地址。引用类型对象变量其实是一个引用,它们的值是指向对象所在的内存地址。
equals方法比较的是对象的内容是否相同
equals()方法存在于Object类中,而Object类是所有类的父类。在Object类中定义了equals方法:
public boolean equals(Object obj) {
return (this == obj);
}
如果类未重写equals方法
调用equals时,会调用Object中的equals方法(实际使用的也是 == 操作符)
如果类重写了equals方法
调用equals时,会调用该类自己的equals方法(一般是比较对象的内容是否相同)。比如:
三者的区别可以从可变性、线程安全性、性能三个角度去分析:
可变性:
String
是不可变的
StringBuilder
与 StringBuffer
都继承自 AbstractStringBuilder
类,在 AbstractStringBuilder
中也是使用字符数组保存字符串,不过没有使用 final
和 private
关键字修饰,最关键的是这个 AbstractStringBuilder
类还提供了很多修改字符串的方法比如 append
方法
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
//...
}
线程安全性:
String
中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder
是 StringBuilder
与 StringBuffer
的公共父类,定义了一些字符串的基本操作,如 expandCapacity
、append
、、insert
、indexOf
等公共方法。
StringBuffer
对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder
并没有对方法进行加同步锁,所以是非线程安全的。性能:
String
类型进行改变的时候,都会生成一个新的 String
对象,然后将指针指向新的 String
对StringBuffer
每次都会对 StringBuffer
对象本身进行操作,而不是生成新的对象并改变对象引用。StringBuilder
相比使用 StringBuffer
仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。String
StringBuilder
StringBuffer
String
类中使用 private 和 final 两个关键字修饰字符数组(作用:保存字符串)final
关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象final
修饰且为私有的,并且String
类没有提供/暴露修改这个字符串的方法。String
类被 final
修饰导致其不能被继承,进而避免了子类破坏 String
不可变。public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
//...
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。