限定符
敲代码过程中常常涉及到一些修饰限定符,如protected, private, public, static, final等等,这些符号在代码里究竟起了什么作用呢?今天来一睹为快。
各限定符功能说明
static
按照字面意思是静态,什么是静态?在java世界里,静态意味着很多,如果你在变量,方法面前修饰了static关键词,那就意味着你可以不创建对象就可以通过类名直接调用该变量或者方法,等等,private,protected这时在底下坐不住了,他们有话说,
private:如果我出手的话,没有人能动我的东西,除了我自己内部的人(方法)
protected:我没private那么自私,只要谁跟我在同一个屋檐下(同一个包内),或者谁继承了我的血脉,都可以使用。至于其他人嘛,嘿嘿。
来看一个例子:
class Mate{
private static String name="Mate8";
static int price=2000;
static protected String realname="HUAWEI";
void showName(){
System.out.println("Mate name is "+name);
}
}
public class StaticTest {
static protected String realname="HUAWEI";
public static void main(String[] args) {
Mate mate=new Mate();
// System.out.println(Mate.name);会报错,不信你走两步
System.out.println(Mate.price);
mate.showName();
System.out.println(Mate.realname);
}
}
要验证protected需要建立一个类放在另外一个包中,这个就留给好学的你。
static除了限定外部访问权限外,他在对象初始化时也起到了作用,同样举个例子:
class TestAncestor{
static String message="I am TestAncestor!";
}
class Cat{
Cat(){
System.out.println("I am cat");
}
}
class Pig{
int weight=200;
Pig(){
System.out.println("My weight is "+weight);
}
static Cat cat=new Cat();
}
public class TestOrder extends TestAncestor{
String what="I am initialized before constructor";
TestOrder(){
System.out.println(what);
}
public static void main(String[] args) {
System.out.println(TestOrder.message);
new TestOrder();
Pig pig=new Pig();
}
}
这里需要涉及到一些对象初始化的知识,顺便带一下,初始化顺序从上到下
- 父类的初始化
- 带static优先执行,初始化
- 变量的初始化
- 构造函数
上面的例子应该能比较好的说明了这个顺序,先看看运行结果:
I am TestAncestor!
I am initialized before constructor
I am cat
My weight is 200
不知是否符合你的期望,其实打个断点便一目了然了。以上例子还是不足以说明全部,还是请出Java编程思想的作者Bruce来现身说法吧,直接上代码:
class Bowl {
Bowl(int marker) {
System.out.println("Bowl(" + marker + ")");
}
void f(int marker) {
System.out.println("f(" + marker + ")");
}
}
class Table {
static Bowl b1 = new Bowl(1);
Table() {
System.out.println("Table()");
b2.f(1);
}
void f2(int marker) {
System.out.println("f2(" + marker + ")");
}
static Bowl b2 = new Bowl(2);
}
class Cupboard {
Bowl b3 = new Bowl(3);
static Bowl b4 = new Bowl(4);
Cupboard() {
System.out.println("Cupboard()");
b4.f(2);
}
void f3(int marker) {
System.out.println("f3(" + marker + ")");
}
static Bowl b5 = new Bowl(5);
}
public class StaticInitialization {
// static Test monitor = new Test();
public static void main(String[] args) {
System.out.println("Creating new Cupboard() in main");
new Cupboard();
System.out.println("Creating new Cupboard() in main");
new Cupboard();
t2.f2(1);
t3.f3(1);
// monitor.expect(new String[] {
// "Bowl(1)",
// "Bowl(2)",
// "Table()",
// "f(1)",
// "Bowl(4)",
// "Bowl(5)",
// "Bowl(3)",
// "Cupboard()",
// "f(2)",
// "Creating new Cupboard() in main",
// "Bowl(3)",
// "Cupboard()",
// "f(2)",
// "Creating new Cupboard() in main",
// "Bowl(3)",
// "Cupboard()",
// "f(2)",
// "f2(1)",
// "f3(1)"
// });
}
static Table t2 = new Table();
static Cupboard t3 = new Cupboard();
} ///:~
Bowl allows you to view the creation of a class, and Table and Cupboard have static members of Bowl scattered through their class definitions. Note that Cupboard creates a non-static Bowl bowl3 prior to the static definitions.
From the output, you can see that the static initialization occurs only if it’s necessary. If you don’t create a Table object and you never refer to Table.bowl1 or Table.bowl2, the static Bowl bowl1 and bowl2 will never be created. They are initialized only when the first Table object is created (or the first static access occurs). After that, the static objects are not reinitialized.
The order of initialization is statics first, if they haven’t already been initialized by a previous object creation, and then the non-static objects. You can see the evidence of this in the output. To execute main( ) (a static method), the StaticInitialization class must be loaded, and its static fields table and cupboard are then initialized, which causes those classes to be loaded, and since they both contain static Bowl objects, Bowl is then loaded. Thus, all the classes in this particular program get loaded before main( ) starts. This is usually not the case, because in typical programs you won’t have everything linked together by statics as you do in this example.
To summarize the process of creating an object, consider a class called Dog:
1.
Even though it doesn’t explicitly use the static keyword, the constructor is actually a static method. So the first time an object of type Dog is created, or the first time a static method or static field of class Dog is accessed, the Java interpreter must locate Dog.class, which it does by searching through the classpath.
2.
As Dog.class is loaded (creating a Class object, which you’ll learn about later), all of its static initializers are run. Thus, static initialization takes place only once, as the Class object is loaded for the first time.
3.
When you create a new Dog( ), the construction process for a Dog object first allocates enough storage for a Dog object on the heap.
4.
This storage is wiped to zero, automatically setting all the primitives in that Dog object to their default values (zero for numbers and the equivalent for boolean and char) and the references to null.
5.
Any initializations that occur at the point of field definition are executed.
6.
Constructors are executed. As you shall see in the Reusing Classes chapter, this might actually involve a fair amount of activity, especially when inheritance is involved.
结合代码和Bruce的说明现在你是不是心中一亮呢?
final
说到final,有人说他是Java世界里忠诚的代名词,因为他规定的东西不允许再改变,可也有人说他始乱终弃,同样都是final怎么会有截然不同的评价呢?来看看下面一段代码:
class Rose{
String heart="I belong to Cal forever!";
@Override
public String toString(){
return "What true idea in Rose is: "+heart;
}
String changeHeart(String heart){
return this.heart=heart;
}
}
public class Titanic {
public static void main(String[] args) {
final int AGE=18;
final Rose rose=new Rose();
//rose=new Rose();
//AGE++
System.out.println(rose);
rose.changeHeart("Jack, you jump, I jump!");
System.out.println(rose);
}
}
有了final后这世上只能有一个rose,当你想让rose指向新的一个对象时编译会无法通过,但这并不代表你不能改变rose的内心,当她遇上了Jack后,一切就都变了。
hashcode与equal作用
以下摘录自:hashcode作用
1、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;
2、如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;
3、如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;
4、两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。
1.hashcode是用来查找的,如果你学过数据结构就应该知道,在查找和排序这一章有
例如内存中有这样的位置
0 1 2 3 4 5 6 7
而我有个类,这个类有个字段叫ID,我要把这个类存放在以上8个位置之一,如果不用hashcode而任意存放,那么当查找时就需要到这八个位置里挨个去找,或者用二分法一类的算法。
但如果用hashcode那就会使效率提高很多。
我们这个类中有个字段叫ID,那么我们就定义我们的hashcode为ID%8,然后把我们的类存放在取得得余数那个位置。比如我们的ID为9,9除8的余数为1,那么我们就把该类存在1这个位置,如果ID是13,求得的余数是5,那么我们就把该类放在5这个位置。这样,以后在查找该类时就可以通过ID除 8求余数直接找到存放的位置了。
2.但是如果两个类有相同的hashcode怎么办那(我们假设上面的类的ID不是唯一的),例如9除以8和17除以8的余数都是1,那么这是不是合法的,回答是:可以这样。那么如何判断呢?在这个时候就需要定义 equals了。
也就是说,我们先通过 hashcode来判断两个类是否存放某个桶里,但这个桶里可能有很多类,那么我们就需要再通过 equals 来在这个桶里找到我们要的类。
那么。重写了equals(),为什么还要重写hashCode()呢?
想想,你要在一个桶里找东西,你必须先要找到这个桶啊,你不通过重写hashcode()来找到桶,光重写equals()有什么用啊