面向对象
类和对象
构造器(构造方法)
- 1.构造器是一个类创建对象的根本途径,如果程序员没有为一个类编写构造器,那么系统会为该类提供一个默认的构造器.一旦程序员为了一个类提供了构造器,系统便不再为该类提供构造器.
- 2.构造器名必须和类名相同,并且不能有返回值(返回值也不能是
void
) - 3.构造函数的作用是完成对象的初始化工作
- 4.构造函数不能被继承,因此它不能被覆盖(重写
override
),但是可以被重载,可以使用不同的参数个数或参数类型来定义多个构造函数 - 5.子类可以通过
super
关键字来调用父类的构造函数,
当父类没有提供无参数的构造函数时,子类的构造函数必须显式的调用父类的构造函数;如果父类提供了无参数的构造函数时,子类的构造函数可以不显式的调用父类的构造函数,这时编译器默认调用父类提供的无参数的构造函数.
当有父类时,在实例化对象时会先执行父类的构造函数,然后再执行子类的构造函数. - 6.当父类和子类都没有定义构造函数时,编译器会为父类生成一个默认的无参数的构造函数,给子类也生成一个默认的无参数的构造函数.构造函数的修饰符和当前类的修饰符有关.
static
static
是一个关键字,它可用来修饰方法,成员变量等成员.static
修饰的成员表明它属于这个类本身,而不属于该类的单个实例.
把static
修饰的成员变量和方法也称为类变量,类方法.不使用static
修饰的普通方法,成员变量则属于该类的单个实例,而不属于该类.
把不使用static
修饰的成员变量和方法称为实例变量,实例方法.
static
修饰的方法和成员变量,既可以通过类来调用,也可以通过实例来调用:没有使用static
修饰的普通方法和成员变量,只可以通过实例来调用.
static
的真正作用是区分成员变量,方法,内部类,初始化块这四种成员到底属于类本身还是属于实例.
- 1.为某种数据类型或对象分配单一的存储空间,而与创建对象的个数无关.
- 2.实现某个方法或属性与类而不是对象关联在一起,也就是说,在不创建对象的情况下就可以通过类来直接调用方法或使用类的属性.
1static
成员变量:java
提供两种类型的变量,用static
关键字修饰的静态变量和不用static
关键字修饰的实例变量.静态变量属于类,在内存中只有一个复制(所有实例都指向同一个内存地址),只要静态变量所在的类被加载,这个静态变量就会被分配空间,因此就可以被使用了.静态变量的引用有两种方式 "类.静态变量"和"对象.静态变量".
实例变量属于对象,只有对象被创建后,实例变量才会被分配空间,才可以被使用,它在内存中存在多个复制,只能用"对象.实例变量"的方式来引用.
2 static
成员方法:static
方法是类的方法,不需要创建对象就可以被调用,而非static
方法是对象的方法,只有对象被创建出来后才可以被使用.static
方法中不能使用this
或super
等关键字,不能调用非static
方法,只能访问所属类的静态成员变量和成员方法.因为当static
方法被调用时,这个类的对象可能还没有被创建,即使已经被创建了,也无法确定调用哪个对象的方法.同理static
方法也不能访问非static
类型的变量.
3static
代码块(静态代码块):是类中独立于成员变量和成员函数的代码块的.它不在任何一个方法体内,JVM
在加载类时会执行static代码块,如果有多个static
代码块,JVM
将会按顺序来执行.
4static
内部类:static
内部类是用来声明为static
的内部类.它可以不依赖于外部类实例对象而被实例化,而通常的内部类需要在外部类实例化之后才能实例化.静态内部类不能与外部类有着相同的名字,不能访问外部类的普通成员变量,只能访问外部类中的静态成员和静态方法.
继承(extends)
通过继承,子类可以使用父类中的一些成员变量和方法,从而提高代码的复用性,提高开发效率.在Java语言中被继承的类叫做父类,继承父类的类称为子类(派生类).
- 1.java语言不支持多继承
- 2.子类只能继承父类的非私有(
public
和protected
)成员变量和方法 - 3.当子类中定义的成员变量和父类中定义的成员变量同名时,子类中的成员变量会覆盖掉父类的成员变量,而不会继承.
- 4.当子类中的方法和父类中的方法有着相同的方法签名(相同的方法名,相同的参数列表),子类会覆盖掉父类的方法而不会继承.
重载(overload)和重写(override)
重载(overload
)是在一个类中多态性的一种表现,是指在一个类中定义了多个同名方法,它们或有不同的参数个数或者有不同的参数类型.
- 1.重载是通过不同的方法参数来区分的,
P122
- 2.不能通过方法的访问权限,返回值的类型和抛出的异常类型来进行重载
- 3.对于继承来说,如果基类方法的访问权限是
private
,那么不能在派生类中对其重载;如果派生类也定义了一个同名的函数,这只是一个新的方法,不会达到重载的效果.
重写(override
)是指派生类方法(函数)覆盖父类方法(函数).覆盖一个方法并对其重写,以达到不同的作用. - 1.派生类中的覆盖方法必须要和基类中的被覆盖的方法具有相同的函数名和参数
- 2.派生类中的覆盖方法的返回值必须和基类中被覆盖的方法的返回值相同
- 3.派生类中的覆盖方法所抛出的异常必须要和基类中被覆盖的方法所抛出的异常一致.
- 4.基类中被覆盖的方法不能为
private
,否则其子类只是定义了一个方法,并没有对其覆盖.
覆盖(重写)是子类和父类之间的关系,是垂直关系;重载是同一个类中的不同方法之间的关系,是水平关系
覆盖要求方法的参数列表相同;重载要求方法的参数列表不同
覆盖关系中,调用方法体是根据对象的类型(对象对应存储空间的类型)来决定;而重载是根据调用时的实参表和形参表来选择方法体的.
多态
多态表示同一个操作作用在不同对象时,会有不同的语义,从而会产生不同的效果.比如"3"+"4"实现了字符串的连接而"3+4"却实现了整数的相加
方法的重载(overload
).重载指的是同一个类有着多个有着不同参数列表的同名方法.因此在编译的时候就可以确定到底调用哪个方法,它是一种编译时的多态.重载可以被看做一个类中的方法的多态性
方法的覆盖(override
).子类可以覆盖父类的方法,因此同样的方法在子类和父类中有着不同的表现形式.在java语言中,基类的引用变量不仅可以指向其实现类的实例对象,也可以执行其子类的实例对象.同理,接口的引用变量也可可以指向其实现类的实例对象.它是一种运行时的多态
public class Duotai {
public Duotai() {
g();
}
public void f() {
System.out.println("Duotai f()");
}
public void g() {
System.out.println("Duotai g()");
}
}
public class Derived extends Duotai{
public void f() {
System.out.println("Derived f()");
}
public void g() {
System.out.println("Derived g()");
}
}
public class Test {
public static void main(String[] args) {
Duotai b=new Derived();
b.f();
b.g();
}
}
输出结果为:
Derived g()
Derived f()
Derived g()
引用变量在编译阶段只能调用其编译时类型所具有的方法,但运行时则执行它运行时类型所具有的方法.比如编写java代码时,引用变量只能调用声明该变量时所用类里包含的方法.比如通过
Duotai b=new Derived();
代码来定义一个引用变量b
,则这个b
只能调用Duotai
类的方法,而不能调用Derived类里定义的方法.
只有类中的方法才有多态的概念,类中成员变量没有多态的概念.成员变量是无法实现多态的,成员变量的值取决于父类还是子类并不取决对于创建对象的类型,而是取决于所定义变量的类型,这是在编译期间确定的
抽象类和接口
抽象类:如果一个类中包含抽象方法,那么这个类就是抽象类.java语言
中通过把类或者类中的某些方法用abstract
修饰表示一个类是抽象类(abstract
只能用来修饰类和方法,不能用来修饰属性).
接口:是一个方法的集合,接口中所有的方法都没有方法体,在java语言
中接口是通过interface
来实现的.
抽象类表示的是一个实体,而接口表示的是一个概念,一个规范.因此接口里不能包含构造器和初始化块的定义.接口里包含成员变量(只能是静态常量),方法(只能是抽象实例方法,类方法和默认方法),内部类(包含内部接口,枚举)定义.
对于接口里定义的静态常量而言,它们都是接口相关的,因此系统会自动为这些成员变量增加static
和final
两个修饰符.也就是说,不管是否使用public static final
修饰符,接口里的成员变量总是使用这三个修饰符来修饰,因为接口中没有构造器和初始化块,因此接口里定义的成员变量只能在定义时指定初值.
//系统自动为接口里定义的成员变量增加 public static final修饰符
int MAX_SIZE=50;
public static final int MAX_SIZE=50;
接口里定义的方法只能是抽象方法,类方法和默认方法,如果不是定义默认方法,系统自动为普通方法增加abstract
修饰符;定义接口里的普通方法时不管是否使用public abstract
修饰符,接口里的普通方法总是使用public abstract
来修饰.接口里的普通方法不能有方法实现(方法体);
//接口里定义的普通方法系统默认添加public abstract修饰符
//并且普通方法不能有方法体
public abstract void onClick(int position);
void onClick(int position);
但类方法,默认方法都必须有方法实现(方法体),并且不论开发者是否指定public
,系统会自动为默认方法/类方法添加public
修饰.(Java8
以上版本才会允许在接口中定义默认方法类方法)
//在接口中定义类方法,需要static修饰
static String staticTest(){
return "接口里的类方法";
}
//接口中定义默认方法,需要使用default修饰
default void test(){
System.out.println("默认的test()方法");
}
接口里定义的内部类,内部接口,内部枚举默认都采用public static
两个修饰符,不管定义时是否指定这两个修饰符,系统都会自动使用public static
对它们进行修饰.
使用接口
接口不能用于创建实例,但接口可以用来声明引用类型的变量,当使用接口来声明引用类型的变量时,这个引用类型变量必须引用到其实现类的对象.接口的主要用途就是被实现类所实现.
- 1.定义变量,也可用于进行强制类型转换
- 2.调用接口中定义的常量
- 3.被其他类实现
实现接口与继承父类相似,一样可以获得所实现接口里定义的静态常量,方法(抽象方法和默认方法(java8
才支持)).
一个类实现了一个或者多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法(也就是重写这些抽象方法),否则,该类将保留从父接口那里继承到的抽象方法,该类也必须定义成抽象类.可以把实现接口当成一种特殊的继承,相当于实现类继承了一个彻底抽象的类.
例子见疯狂java讲义P194
只要包含一个抽象的方法的类就必须声明为抽象类,抽象类可以声明方法的存在而不去实现它,被声明为抽象的方法不能包含方法体.在实现时,必须包含相同或者更低的访问级别(public --protected---private
).抽象类在使用的过程中不能实例化,但是可以创建一个对象使其指向具体子类的一个实例.抽象类的子类为父类中的所有抽象的方法提供实现,否则它们也是抽象类.接口中的所有方法都是抽象的,可以通过接口来间接地实现多重继承.接口中的成员变量都是static final
类型
接口和抽象类的共同点:
- 1.都不能被实例化
- 2.接口的实现类或抽象类的子类都只有实现了接口或抽象类中的抽象方法后才能被实例化.
接口和抽象类的不同点:
- 1.接口需要实现(
implements
)而抽象类只能被继承(extends
).一个类可以实现多个接口,但一个类只能继承一个抽象类.因此使用接口可以间接的达到多重继承的目的. - 2.接口强调特定功能的实现"
has - a
",而抽象类强调所属关系"is - a
" - 3.接口中定义的成员变量默认是
public static final
,只能够有静态的不能被修改的数据成员,而且,因为接口中没有构造器和初始化块,因此接口里定义的成员变量只能在定义时指定初值,其所有的成员方法都是public abstract的,并且只能被这两个关键字修饰,而抽象类可以有自己的数据成员变量,也可以有非抽象的成员方法,抽象类中的成员变量默认为default
(本包可见),当然也可以被定义为private
,protected
和public
,这些成员变量可以在子类中重新定义,也可以重新被赋值.抽象类中的抽象方法不能用private static synchronized native
等修饰符修饰,同时方法必须以分好结尾,不能有花括号 - 4.接口被运用于实现比较常用的功能,便于日后维护或者添加删除方法;而抽象类更倾向于充当公共类的角色,不适用于日后重新对立面的代码进行修改
- 5.接口可以继承接口,抽象类可以实现接口,抽象类也可以继承具体类.抽象类也可以有静态的
main
方法. - 6.接口只有定义,其方法不能在接口中实现,只有实现接口的类才能实现接口中定义的方法;而抽象类可以有定义和实现,即其方法可以在抽象类中被实现.
this和super
this
用来指向当前实例对象.
对于static
修饰的方法而言,则可以使用类来直接调用该方法,如果在static
修饰的方法中使用this
关键字,则这个关键字就无法指向合适的对象.static
修饰的方法中不能使用this
引用.java
语法规定:静态成员不能直接访问非静态成员
- 1.在
setter
方法中区分对象的成员变量与方法的形参 - 2.在一个构造器中调用另一个重载的构造器使用
this
super
可以用来访问父类的方法或成员变量,在子类中调用父类被覆盖的实例方法,super
不能出现在含有static
修饰的方法中,static
修饰的方法是属于类的,该方法的调用者可能是一个类,而不是对象,因而super限定也就失去了意义.
构造器中使用super
,则super
用于限定该构造器初始化的是该对象从父类继承得到的实例变量,而不是该类自己定义的实例变量.
- 1.为了在子类方法中访问父类中定义的,被隐藏的实例变量,或为了在子类方法中调用父类中定义的被覆盖的方法,可以通过
super
作为限定来调用这些实例变量和实例方法. - 2.调用父类构造器:在子类构造器中调用父类构造器使用
super
调用完成.使用super
调用父类构造器必须出现在子类构造器执行体的第一行,而this
如果调用另一个重载的构造器也必须要位于该构造器的第一行,所以this
和super
不会同时出现.
final/finally/finalize
final
用于声明属性,方法和类表示属性不可变,方法不可覆盖和类不可被继承(不能在派生出子类)
final
修饰基本类型变量和引用类型变量的区别
当final
修饰基本变量类型时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变
当final
修饰引用类型变量时,final
只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但是这个对象(对象的非final
成员变量的值可以改变)完全可以发生改变.所以被final
修饰的变量必须初始化.
初始化的几种方式:
- 1.在定义的时候初始化
- 2.
final
成员变量可以在初始化块中初始化,但不可以在初始化块中初始化 - 3.静态
final
成员变量可以在初始化块中初始化,但是不可以在普通初始化块中初始化. - 4.在类的构造器中初始化,但静态
final
成员变量不可以在构造函数中初始化.
final
方法:当一个方法被final
修饰的时候,该方法不允许任何子类重写这个方法,但子类仍然可以使用这个方法.
final
参数:用来表示这个参数在这个方法内部不允许修改
final
类:当一个类被声明为final
时,这个类不能被继承,所有方法都不能被重写.
finally
作为异常处理的一部分,它只能在try/catch
语句中,并且附带一个语句块,表示这段语句块最终一定会被执行,经常用在需要释放资源的情况下.
finalize
是Object
类的一个方法,在垃圾回收器执行时会调用被回收对象的finalize()
方法,可以覆盖此方法来实现对其他资源的回收,例如关闭文件等.需要注意的是.一旦垃圾回收器准备好释放对象占用的空间,将会首先调用其finalize()
方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存.
switch
switch
语句用于多分支选择,在使用switch(expr)
时,expr
只能是一个枚举常量(内部也是由整型或字符类型实现)或一个整数表达式,其中整型表达式可以是基本类型int
或其对应的包装类Integer
,当然也包括不同的长度整型例如:short
.而byte
.short
和char
类型的值都能被隐式地转换为int
类型,因此这些类型和它对应的包装类也能作为switch
的表达式;但是long
double
float
String
类型不能隐式地转换为int
类型如果还想用他们的话必须要进行显式转型为int
才可以继续使用!!
case
语句后是直接的常量数值.
从java7开始,switch开始对String类型
提供支持.