foreach循环可以用于数组和任何实现了Iterable接口的类(这并不意味着数组肯定也是一个Iterable)。
▉类和对象
java中的所有东西都包含在类中,在java源文件中可以包括多个类,但是只能有一个public类,而且如果有public类的话,这个文件的名字要和这个类的名字一样。
一个类中可以有多个方法,一个方法中可以有多个语句。
Java存取权限和存取修饰符:
Java有4种存取权限和3种存取修饰符,因为有一个缺省的权限等级(无修饰符)。
4种存取权限:
public:
...
private:
...
default:
只有在同一个包中的默认事物能够存取
protected:
受保护的部分,运行起来像是default,但也允许不在同一个包中子类继承受保护的部分
3种存取修饰符:
public、private、protected
对象与引用:
java中没有所谓的对象变量,有的只是对象的引用,对象只存在于堆上,并且只能通过其引用进行引用。
java创建对象不能通过类似于“Aa;”的形式直接定义,这只是定义了一个对象引用变量,而需要通过类似于“Aa=newA();”的形式在堆上定义一个对象,并将在栈上的对象的引用指向这个对象。
c++中对象作为参数,是按照值传递。
其实java也是值传递,只不过java中只有对象的引用,而没有所谓的“对象变量”,故而传递的都是对象的引用的值,因此在函数中可以改变引用参数指向的对象。
注:
java中String类型的对象作为函数参数传递时,也是传递引用的值,而不是所谓的“值传递”,因为String类是不可变的,它没有提供自身修改的函数,每次修改操作都是新生成一个String对象,所以要特殊对待,可以认为是传值。
关于这一点,网上实在是有太多的流言蜚语,有很多流言是错误的,请务必注意。
那么有没有办法使函数不能够修改引用参数指向的对象呢?
答案是传递对象副本的引用。
为了使用对象的副本作为函数参数,可以使用对象的clone()方法对其进行浅层复制(具体细节请XX之),所谓的浅层复制即是只复制对象本身,而不对其引用的或者其成员变量引用的对象进行复制,也即不对整个对象网进行复制。
对对象网进行复制(深层复制),另有它法,请XX之。
对象相等性的判断:
“==”或者“!
=”只能用来判断对象的引用是否相等,若需判断对象是否相等,则需equals(),但一般你需要覆盖equals(),因为Object中的equals()默认使用对象的地址判断对象是否相等。
但基本类型直接使用“==”或者“!
=”即可。
实例变量与局部变量:
实例变量是声明在类内,而不是方法中,实例变量有默认值。
局部变量是声明在方法中的,局部变量没有默认值,在使用之前,必须初始化,否则编译器报错。
关于初始化的更多细节:
java中成员变量可以在定义的后边直接进行初始化,也可以在构造函数中进行初始化。
而c++中成员变量不能在定义类时直接初始化,需通过构造函数初始化。
final:
声明一个类或方法到达继承树的末尾,可以用final,也即禁止该类被继承或方法被覆盖。
同时请注意final方法是前期绑定的,将方法声明为final可以有效关闭该方法的动态绑定机制,但多数情况下并不会提升多少效率,因此不要为了提升效率而随意使用final来声明方法。
静态的方法:
用static声明的方法是静态的方法,静态方法不需要实例化的对象,就可以通过类名来引用(当然也可以通过对象来引用),但静态的方法不可以使用非静态的变量,也不能调用非静态的方法。
静态的变量:
用static声明的变量是静态变量,静态变量为该类的所有对象共享,注意static不能作用于局部变量,只能作用于成员变量,因此只有成员变量才能是静态变量。
静态变量也会有默认值,就像实例变量一样会被自动初始化一样。
静态变量会在该类任何的静态方法被调用之前被初始化,也会在该类任何的对象被创建之前被初始化。
你可以像初始化其他成员变量一样,直接在定义处初始化,也可以使用特殊的语法结构进行初始化——静态块,如下:
publicclassSpoon{
staticinti;
static{
i=100;
}
}
还有一种类似的初始化非静态成员变量的语法——实例初始化块,只是没有static关键字而已。
静态的final变量:
静态的final变量是常数。
其声明形式为:
publicstaticfinalvar_name;静态的final变量必须被显式初始化,可以用以下两种方法之一初始化:
1、声明时赋值:
publicstaticfinaldoublePI=3.1415926;
2、在静态初始化程序中:
publicstaticfinaldoublePI;static{PI=3.1415926;}
Java终极对象:
在java中的所有类都是从Object这个类继承出来的,也就是说Object是所有类的父类,它是非抽象的,允许被实例化。
你可以显式声明类的继承对象为Object:
publicclassAnimalextendsObject{},如果不显式声明的话,java编译器会隐含的进行继承。
ArrayList就是通过声明Object对象为方法参数来处理所有的对象的,否则他不可能实现处理所有对象的功能。
super与this:
通过super可以调用父类中的方法,例如:
super.isEmpty()。
通过this可以调用本类中的方法,例如:
this.fun()。
实际上,java会将this作为一个隐含的参数传递给类中的每一个方法。
构造函数:
在构造器中调用具有多态行为的函数时,容易引起隐蔽的错误。
为此,这里有一条有效的准则:
“用尽可能简单的方法使对象进入正常状态,如果可以的话,避免调用其他方法,尤其是别调用具有多态行为的方法”。
可以通过super()调用父类的构造函数,如果子类没有显式的调用父类的构造函数,则编译器会在子类的构造函数第一行加上super()调用,当然这里只能调用其父类的无参数的构造函数。
当然你也可以自己写出具有参数的super()函数来调用父类的带参构造函数。
可以通过this()从某个构造函数中调用同一个类的另外一个构造函数。
this()只能存在于构造函数中,且同super()一样,只能位于第一行语句。
因此super()与this()不可兼得。
垃圾回收器和对象清理:
对于java中的垃圾回收器和对象的清理工作,请记住:
垃圾回收器可能会被调用,也可能不会被调用,即使其被调用,它也可能以它想要的任何方式来回收垃圾。
因此,对它不要抱过多幻想。
当下,除了回收内存以外的其他任何事情(比如:
已经打开的文件、网络连接、在屏幕上绘制的图形、甚至可以是外部世界的某个开关),你最好不要依赖于垃圾回收器,也不要依赖于finalize()(finalize()是被垃圾回收器调用的,使程序猿可以在垃圾回收器运行时做出一些重要的动作,比如说回收一些非正常方式分配的内存),最好的办法就是程序猿自己编写好特定的方法调用即可,比如在finally中调用之。
▉抽象类、抽象方法
抽象类:
声明形式为:
abstractclassDogextendsAnimal{}
抽象方法:
抽象方法允许你声明方法的名称、参数以及返回类型,但没有实体,直接以分号结束。
例:
publicabstractvoideat();抽象方法的意义是就算无法实现出方法的内容,但还是可以定义出一组子型共享的协议。
注意:
①如果你声明一个抽象方法,那么也必须将该类标记为抽象的,你不能在非抽象类中拥有抽象的方法。
②第一个具体子类必须实现所有的抽象方法。
▉内部类
内部类可以使用外部类所有的方法与变量,就算是私有的,也照样可以使用。
当然,这是说内部类的实例可以存取它所属的外部类实例的所有方法与变量。
一个内部类被嵌套多少层并不重要,它都能够透明地访问所有它所嵌入的外围类的所有成员。
在外部类中创建内部类的实例,就像普通的类的创建一样,例如:
MyInnerinner=newMyInner();
在外部类以外的程序代码创建内部类的实例,要用特殊的语法:
MyOuterouter=newMyOuter();MyOuter.MyInnerinner=outer.newMyInner();
如果你需要生成对外部类对象的引用,可以使用外部类的名字后边紧跟圆点和this的特殊语法,如:
MyOuter.this
使用内部类最吸引人的原因是:
每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
也就是说内部类允许以继承多个非接口类型(类或者抽象类)的方式来实现“多重继承”。
▉匿名内部类
Objectobject=newObject(){
...
};
以上代码创建了一个继承自Object的匿名内部类的对象。
如果初始化匿名内部类的成员变量,可以直接在变量定义的后边初始化,也可以使用类似于构造器的方法初始化——实例初始化块(匿名内部类没有名字,所以也不可能有构造器)。
但在初始化时,如果匿名内部类需要用到外部方法中的变量(外部方法的形参或者外部方法的局部变量),那么外部方法中的该变量必须声明为final的。
同时注意:
匿名内部类既可以继承类,也可以扩展接口,但是不管是什么,一次只能一个啊。
▉静态类
静态类包含在外部类中,但用static修饰,在实例化时不需要有外部类的实例。
▉重载与覆盖
java中,派生类可以随意重载基类中已经拥有的方法,并且使用基类中已经拥有的方法和使用自己重载的方法一样容易,但是如果你想覆盖基类中的方法时最好加上注解:
@Override,以防止弄错了,没有覆盖,反而成了重载。
注意方法的类型由方法名和参数列表和参数类型组成,方法返回值和方法名后边跟的异常说明不属于方法类型的一部分。
▉组合与继承
组合:
就是在类中定义其他类的引用,并使用之。
继承:
声明形式为:
publicclasschild_nameextendsfather_name{}
在面向对象的编程中,要谨慎使用继承,因为继承可能会导致脆基类问题,也即当你更改超类时可能会破坏子类。
关于应该使用组合还是使用继承,看看是否需要向上转型,如果需要则考虑使用继承,如果不需要则考虑要不要使用继承。
▉多态
java中除了static方法(构造器实际上也是static方法,只不过该static声明是隐式的)和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定。
多态正是基于此机制。
同时请注意,对于成员变量并不具有多态性啊。
▉接口(interface)
类可以有多个接口,接口的意义在于多态!
在C++中允许多重继承,即一个子类可以有多个父类;但在Java中是不允许一个子类有多个父类的,这样也是为了简单化,防止致命方块的复杂性。
接口可以用来解决多重继承的问题,却也不引起致命方块的问题。
接口解决致命方块的方法很简单:
那就是把全部的方法设置为抽象的,就相当于100%的纯抽象类。
接口允许创建者确定方法的名字、参数、以及返回类型,但是没有任何方法体,以分号结束。
接口也可以包含域,但是这些域隐式的是static和final的。
静态类也可以作为接口的一部分,放到接口中的任何类都自动地是public和static的。
接口的定义:
publicinterfacePet{},定义Pet类为接口。
接口的实现:
publicclassDogextendsAnimalimplementsPet{},Dog继承自Animal,以Pet为接口。
注意:
一个实现该接口的具体类必须实现该接口中的所有方法。
▉枚举(enum)
暂时还没东东呢
▉类型信息
Java允许运行时识别对象和类的信息,运行时类型信息识别主要有两种方式:
RTTI、反射(折射折射你在哪儿)。
RTTI:
通过RTTI(Run-TimeTypeInformation,运行时类型信息),程序能够使用基类的引用来检查这些引用所指的对象的实际派生类型。
同时注意:
RTTI实际上允许我们对特定的类进行特定的处理,这对于有过程化编程经验的程序猿来说,是不是很合口味?
但是我们在面向对象的编程中,应该尽量使用多态机制,来进行普适操作,然而使用多态机制要求你对基类拥有控制权。
因此对于RTTI和多态要进行合理取舍。
Java的RTTI与Class对象密切相关。
Class:
Class是java中的一个类(java.lang.Class),只不过名字有点特殊罢了。
那么这个类抽象了什么?
它的实例又表示了什么呢?
在一个运行的程序中,会有许多类和接口存在。
我们就用Class这个类来表示对这些类和接口的抽象,而Class类的每个实例则代表运行中的一个类。
所谓抽象,就是提取这些类的一些共同特征,比如说这些类都有类名,都有对应的hashcode,可以判断类型属于class、interface、enum还是annotation。
需要注意的是,这个特殊的Class类没有公共构造方法。
Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass方法自动构造的,因此不能显式地声明一个Class对象。
虚拟机为每种类型管理一个独一无二的Class对象。
也就是说,每个类(型)都有一个Class对象。
运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。
如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。
基本的Java类型(boolean、byte、char、short、int、long、float和double)和关键字void也都对应一个Class对象。
每个数组属于被映射为Class对象的一个类,所有具有相同元素类型和维数的数组都共享该Class对象。
一般某个类的Class对象被创建,它就用来创建这个类的所有对