Java基础:Java核心技术提示的易忽略点 - new JinhaoPlus()
开篇
Java是一门不那么简单也不那么复杂的语言,Java里面有很多问题和特性是容易被使用者忽视的,这些问题也许会难住新手,同时也许会是老手不小心跌入的无故之坑,只有精于对基础的提炼才能最大程度地解决类似的疑问。所以,在读Cay.Horstmann的《Java核心技术》的过程中,我记录下这些所谓的易忽略的问题,这些问题将会持续更新在我的这个Segment Fault的博客下,也算是激励自己重新挖掘这些基础问题的内涵。这个博客将以原书中的章节为分割,大概会是每章一篇,持续更新,每篇的内容也不会一次全部写完,视我个人对问题的理解和我的阅读进度而定。
另外,我们从本书第一卷第四章开始,因为前三章无外乎就是在讲述历史和环境配置,我认为和我们的主题不大相关。
Core Java Volume 1
chap4
setter的不可靠性
理论上我们将域属性设置为private的目的是为了不让程序能够轻易地访问和修改到这个对象私有的属性,而只能通过该类定义的public的getter和setter方法去访问和修改。但是这并不是完全可靠的,程序仍然可以通过一些灵巧的办法在某些合适的情况下获取和更改这些私有的属性值。
setter的不可靠性,其实只要能够确定这个私有属性的引用,是完全可以直接做更改的,当然前提是获取到这个引用,比如通过getter拿到这个属性的引用。这是一个例子:
class Secret{ private StringBuffer secret = new StringBuffer("A Secret"); public StringBuffer getSecret() { return secret; } public void setSecret(StringBuffer secret) { this.secret = secret; }}public class TestClass { public static void main(String[] args) { Secret secret = new Secret(); StringBuffer saidSecret = secret.getSecret(); saidSecret.append(":broken"); System.out.println(secret.getSecret()); }}
当然。这个例子其实是个特例,因为我们获取到的私有属性的引用是StringBuffer类型,是可修改内容的引用,但是如果是String类型的secret,那么我们就没办法这样绕过setter去修改了,因为String类型的引用是不允许修改原值的。
private的访问权限
我们都知道的是private属性是私有的,那么到底是对象私有的还是类私有的呢?答案是后者,我们可以在类定义的内部访问私有属性,即使这个属性是其他对象的,只要是同类的即可,虽然我们很少这么使用,但是这却深刻地说明java中的访问权限在类的范畴上。下面说明了这样一个问题:
class Secret { private String secret; public String getSecret() { return secret; } public void setSecret(String secret) { this.secret = secret; } public void readOtherSecret(Secret otherSecret) { System.out.println(otherSecret.secret); }}public class TestClass { public static void main(String[] args) { Secret secret1 = new Secret(); secret1.setSecret("secret A"); Secret secret2 = new Secret(); secret2.setSecret("secret B"); secret1.readOtherSecret(secret2); secret2.readOtherSecret(secret1); }}
static究竟是什么意思
static的本意是静态的,在java里面这个静态的意思是指存在于静态的类中的,而非程序开跑之后运行时的动态对象的,所以static的域属性也好,方法也好,都是指那些存在于类中的域属性和方法,虽然该类对应的对象中也存在static域属性和方法的拷贝,但是属于类的,对于它们的影响将会影响到整个类,换言之这些影响将会产生在所有的类对象中。
static的方法只能访问static的域属性,这个是怎么来的呢?其实就是因为static属性是属于整个类的,所以static的方法就不应该影响特定的对象的属性,在实现上说这个方法的隐式参数是不含有非static的方法的指向当前对象的this引用的,因此不能在static方法中访问非static属性。
java的方法参数传递
在C++中被反复向初学者讨论的一个问题就是swap函数的效果,如果参数是值传递的,则最终传入的两个值是不会被交换的,如果是指针(引用)传递的,那么最终传入的两个引用是会被交换的,那么java中的方法参数传递是哪种情况呢?答案是前者,即值传递,又叫做拷贝实参传递,即运行在方法中的形式参数是传递给方法的真实参数的拷贝,而非直接传入实际参数的指针。
下面的例子说明了这个问题:
class Some{ private String someThing; public String getSomeThing() { return someThing; } public void setSomeThing(String someThing) { this.someThing = someThing; } public Some(String someThing) { super(); this.someThing = someThing; }}public class TestClass { public static void swap(Some some1,Some some2){ Some temp = some1; some2 = some1; some1 = temp; } public static void main(String[] args) { Some some1 = new Some("1"); Some some2 = new Some("2"); swap(some1, some2); System.out.println(some1.getSomeThing()); System.out.println(some2.getSomeThing()); }}
类和对象初始化
我们都知道的是对象的初始化会引发类的初始化,初始化实现的方法也很多,最一般的初始化方法用于对象创建时对域属性的初始化,稍微少见的类的静态代码块用于对static静态域属性进行初始化,而初始化代码块则用于对象的域属性(包括static域属性)进行初始化,那么一般而言,初始化的顺序是怎样的呢?
首先执行静态代码块初始化static类静态域属性用于在整个类及其对象共享;
其次执行初始化代码块初始化域属性以完成对对象的个性化的域属性的初始化;
最后执行对象的初始化方法以完成进一步的域属性的初始化。
class Some{ private String someThing; private static String staticSomeThing; static { staticSomeThing = "static"; System.out.println("static block triggered"); } { someThing = "some"; System.out.println("init block triggered"); } public String getSomeThing() { return someThing; } public void setSomeThing(String someThing) { this.someThing = someThing; } public Some(String someThing) { this.someThing = someThing; System.out.println("init method triggered"); }}public class TestClass { public static void main(String[] args) { Some some1 = new Some("1"); }}