欢迎关注我的公众号【软件大爆炸】
Java学习中的碎碎念
Java中易忽略的基础知识
Java面向对象基础
Java中的核心类
Java抽象类和接口
Java中的异常
Java中的泛型与集合
Java为其8个基本数据类型提供了对应的封装类。基本类型对应的封装类除了 Integer
和 Character
写法有点例外,其他的基本类型对应的封装类都是将首字母大写即可。
public class Test {
public static void main(String[] args) {
Integer objInt = new Integer(10);
Character objChar = new Character('y');
Float objFloat = new Float(89.87);
byte num = 100;
Byte objByte = new Byte(num);
Long objLong = new Long(1000L);
short num2 = 100;
Short objShort = new Short(num2);
Double objDouble = new Double(122.3d);
boolean flag = false;
Boolean objBoolean = new Boolean(flag);
}
}
还可以通过传入一个数值字符串进行构造
例如
Integer objInt = new Integer("10");
等价于
Integer objInt = new Integer(10);
如果将对象赋值给基本数据类型则需要调用封装类的xxxValue()
方法
例如:int num = objInt.intValue();
从JDK1.5之后,Java提供了自动装箱(Autoboxing)和自动拆箱( Auto Unboxing)功能,因此,基本类型变量和封装类之间可以直接赋值。
装箱是指将基本类型数据值转换成对应的封装类对象,即将栈中的数据封装成对象存放到堆中的过程。
拆箱是装箱的反过程,是将封装的对象转换成基本类型数据值,即将堆中的数据值存放到栈中的过程。
public class Test {
public static void main(String[] args) {
Integer objInt = 10;
int num = objInt;
}
}
将字符串的值转换为基本类型的值有两种方式:
- 直接利用封装类的构造方法,即Xxx( String s)构造方法;
int num = new Integer("100");
- 调用封装类提供的 parseXxx( String s)静态方法
int num = Integer.parseInt("100");
将基本类型的值转换成字符串有三种方式:
- 直接使用一个空字符串来连接数值即可
String str = ""+12;
- 调用封装类提供的 tostring()静态方法
String num = Integer.toString(100);
- 调用 String类提供的 valueof()静态方法
String num = String.valueOf(100);
关于Object.clone()
方法
代码示例:
//第一个示例
class Student18 implements Cloneable{
int rollno;
String name;
Student18(int rollno,String name){
this.rollno=rollno;
this.name=name;
}
public Object clone()throws CloneNotSupportedException{
return super.clone();
}
public static void main(String args[]){
try{
Student18 s1=new Student18(101,"amit");
Student18 s2=(Student18)s1.clone();
System.out.println(s1.rollno+" "+s1.name);
System.out.println(s2.rollno+" "+s2.name);
}catch(CloneNotSupportedException c){}
}
}
//第二个示例(与第一个没有关系)
Person p = new Person(23, "zhang");
Person p1 = (Person) p.clone();
System.out.println(p);
System.out.println(p1);
clone()方法省去了创建对象精确副本的额外处理任务。 如果使用new关键字执行此操作,则将花费大量处理时间,这就是我们使用对象克隆的原因。
那么在java语言中,有两种方式创建对象:
- 1.使用new操作符创建一个对象
详细步骤:分配内存空间,调用构造函数,初始化,构造方法返回
- 2.使用clone方法复制一个对象
详细步骤:分配内存空间,调用
clone()
函数,使用原对象中对应的各个域填充新对象的域,clone方法返回。
很多朋友会对这两句代码产生疑问
Person p1 = new Person(1001,"xiaoming");
Person p2 = p1;//复制对象的第一种方式:直接采用地址赋值
Person p3 = (Person) p1.clone();//第二种方式:采用Object的clone()方法
下面是上面代码的完整版
public class Person implements Cloneable{
private int id;
private String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person(1001,"xiaoming");
Person p2 = p1;//复制对象的第一种方式:直接采用地址赋值
Person p3 = (Person) p1.clone();//第二种方式:采用Object的clone()方法
System.out.println(p1);
System.out.println(p2);
System.out.println(p3);
}
}
输出的结果如下:
club.BOOKJava8.Person@5fd0d5ae
club.BOOKJava8.Person@5fd0d5ae
club.BOOKJava8.Person@2d98a335
首先我们可以确定是,p1和p2是同一对象,他们的对象id相同。p2和p3并不是同一个对象,他们的对象id并不相同。
我用内存图来展示一下:
首先是Person p2 = p1;//复制对象的第一种方式:直接采用地址赋值
另外一种方式是Person p3 = (Person) p1.clone();//第二种方式:采用Object的clone()方法
另外需要注意的是clone()
方法采用的是浅拷贝,如果需要深拷贝则需要将clone方法重写。
可以参考这篇文章:传送门
关于Object.finalize()
方法
Java有垃圾回收器负责回收无用对象占据的内存资源。但也有特殊情况:假定你的对象(并非使用new)获得了一块“特殊”的内存区域,由于垃圾回收器只知道释放那些经由new分配的内存,所有它不知道如何释放该对象的这块“特殊”内存。为了应对这种情况,Java允许在类中定义一个名为finalize()的方法。它的工作原理“假定”是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。所以要是你打算用finalize(),就能在垃圾回收时刻做一些重要的清理工作。——节选自《Java编程思想》
public class Person implements Cloneable{
private int id;
private String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public static void main(String[] args) throws Throwable {
Person p1 = new Person(1001,"xiaoming");
p1.finalize();//调用finalize方法
}
}
关于Object.getClass()
方法
关于Object.hashCode()
方法
关于Object.toString()
方法:返回包含类名和散列码的字符串,形如:类名@哈希代码值
public class Person implements Cloneable{
private int id;
private String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public static void main(String[] args) throws Throwable {
Person p1 = new Person(1001,"xiaoming");
System.out.println(p1.getClass());
System.out.println(p1.hashCode());
System.out.println(p1.toString());
}
}
class club.BOOKJava8.Person
1607521710
club.BOOKJava8.Person@5fd0d5ae
可以将Object.toString()
进行重写
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
则此时输出的结果为:
Person{id=1001, name='xiaoming'}
下面介绍Java中的字符串类:
有三种,其中不可变的是String
,可变的是StringBulider
和StringBuffer
。
首先介绍String
现在有String str1 = "abc";
此时若接着写:String str1 = "def";
,千万记住,String创建的字符串是不可变的,刚才这么写其实际是在内存中创建一个新的字符串,字符串变量将引用新创建的字符串地址,而原来的字符串在内存中依然存在且内容不变,直至Java的垃圾回收系统对其进行销毁。
另外需要注意:String str2 = new String("lmn")
创建的字符变量在堆中而不是常量池中,new出来的对象都是在堆中。
面试题:String str4= new String(abc")创建多少个对象?
- 在常量池中查找是否有"abc对象
- 有则返回对应的引用实例
- 没有则创建对应的实例对象
- 在堆中new—个 String(“abc”)对象
- 将对象地址赋值给str4创建一个引用所以,常量池中没有"abc"字面量则创建两个对象,否则创建一个对象,以及创建一个引用
变式:
String str3= new String("A"+"B");
会创建多少个对象?
- 常量池中:“A”“B”,“AB”:3个
- 堆中:new String(“AB”):1个
- 引用:str1:1个
- 总共:5个
接下来介绍StringBuffer
String Buffer
创建的字符串是可变的。可以通过append()
,insert()
,setCharAt()
等方法进行操作。字符穿所引用的地址一直没有发生变化。要想获得StringBuffer
的最终内容,可以调用toString()
方法转化成一个String
对象。
public class Test{
public static void main(String args[]){
StringBuffer sBuffer = new StringBuffer("abc");
System.out.println(sBuffer);
sBuffer.append("def");
System.out.println(sBuffer);
sBuffer.insert(1, 'i');
System.out.println(sBuffer);
sBuffer.setCharAt(1,'o');
System.out.println(sBuffer);
}
}
abc
abcdef
aibcdef
aobcdef
还有StringBuilder
StringBuffer
是线程安全的,而 StringBuilder
没有实现线程安全的,因此性能较好。如果只需要创建一个内容可变的字符串对象,不涉及线程安全、同步方面的问题,应优先考虑使用 Stringbuilder
类。
public class Test{
public static void main(String args[]){
StringBuilder sBulider = new StringBuilder("abc");
System.out.println(sBulider);
sBulider.append("def");
System.out.println(sBulider);
sBulider.insert(1, 'i');
System.out.println(sBulider);
sBulider.setCharAt(1,'o');
System.out.println(sBulider);
}
}
abc
abcdef
aibcdef
aobcdef