主要内容
详细内容
概念
什么是面向对象?
面向对象是相对于面向过程而言的,面向过程强调的是功能,而面向对象强调的是将功能封装成对象,强调具备功能的对象。
面向对象的优点
(1)提高代码的复用性;
(2)将复杂的事情简单化了;
(3)将程序员由执行者变成了指挥者,符合人们思考习惯的一种思想。
类与对象
类和对象的定义
类是用来描述对象的。是对某一批对象的抽象,可以把对象理解成某种概念。
对象是一个具体存在的实体。
类和对象的关系
对象是类的实例,类是对象的抽象。
如何定义类?
将一系列特征相似的对象的共同特征以及行为抽取出来进行描述,写在一个class中,用成员变量描述对象的特征,用成员方法来描述对象的行为;具体格式如下所示:
[修饰符]class 类名
{
零到多个构造器定义...
零到多个属性...
零到多个方法...
}
其中:构造器的定义方式如下所示:
[修饰符] 构造器名(形参列表)
{
//由零条到多条可执行语句组成的构造器执行体
}
说明:
(1)修饰符:可以省略,也可以是public、protected、private之一;
(2)构造器名:必须和类名相同;
(3)形参列表:有零组到多组“参数类型 形参名”组合而成,多组参数之间用英文逗号隔开,形参类型和形参名之间使用英文空格隔开。
属性的定义方式如下所示:
[修饰符] 属性类型 属性名 [= 默认值];
说明:
(1)修饰符:可以省略,也可以是public、protected、private之一,可以与static、final组合来修饰属性;
(2)属性类型:可以是Java语言允许的任何数据类型,包括基本类型和应用类型;
(3)属性名:只要是合法的标识符,建议由一个或多个单词连缀而成,第一个单词首字母小写,后面每个单词首字母大写,其余字母小写。
方法的定义方式如下所示:
[修饰符] 方法返回值类型 方法名(形参列表)
{
//由零条到多条可执行语句组成的方法体
}
说明:
(1)修饰符:可以省略,也可以是public、protected、private之一;abstract和final中罪错出现其中之一,它们可以与static组合起来修饰方法。
(2)方法返回值类型:可以是Java语言允许的任何数据类型,包括基本类型和应用类型;
(3)方法名:和属性名基本相同,但通常建议方法名以英文中动词开头;
(4)形参列表:和构造器中形参列表格式基本相同,但是一旦在定义方法时指定了形参列表,则调用该方法时必须传入对应的参数值——谁调用方法,谁负责为形参赋值。
创建对象以及原理
使用new关键字和指定的类名来创建一个对象;是在堆内存中创建了一个对象,然后在栈内存中创建一个变量引用了对象的地址。
成员变量初始化
当一个变量被创建的时候,会对各种类型的成员变量自动进行初始化赋值;基本数据类型初始化值为0,引用数据类型初始化值为null;如下图所示:
对象的生命周期
对象的生命周期从new关键字创建开始,到没有任何引用到达对象时结束(称为垃圾)。
在以下的对象的状态转换图中有以下三种状态:
(1)可达状态:当一个对象被创建后,若有一个以上的引用变量在引用它,则这个对象就在程序中处于可达状态,程序通过引用变量来调用该对象的属性和方法;
(2)可恢复状态:如果程序中某个对象不再有任何引用变量引用它,它就进入了可恢复状态;在回收该对象之前,系统会调用所有可恢复状态对象的finalize()方法进行资源清理。如果系统在调用finalize()方法时重新让一个引用变量引用该对象,则这个对象会再次变为可达状态,否则该对象将进入不可达状态;
(3)不可达状态:当对象与所有引用变量的关联都被切断,且系统已经调用所有对象的finalize()方法后依然没有是该对象变成可达状态,那么该对象将永久性失去引用,最后编程不可达状态。只有一个对象处于不可达状态时,系统可以使用System.gc()方法或者Runtime.getRuntime().gc()方法来回收该对象所占有的资源。
匿名对象
匿名对象就是没有名字的对象,是对象的一种简写形式;因为匿名对象没有任何引用到达,在使用一次之后便成为了垃圾。
通常我们需要使用一个对象且只使用一次对象的时候,就可以使用匿名对象。比如将对象作为一个参数传递给另一个函数。
特征
封装(Encapsulation)
(1)定义:指将对象的状态信息隐藏在对象的内部,不允许外部程序直接访问对象的内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。
(2)如何定义:将对象的属性和实现细节隐藏起来,不允许外部直接访问;提供公共方法对其访问。
(3)优点:
a、提高了安全性:在访问对象的属性时候通过方法来实现,在方法中可以进行校验;
b、简化编程:使用者无须关心对象内部具体实现细节,只要根据对象功能调用指定方法便行。
继承(Inherit)
(1)定义:在程序中,使用extends关键字让一个类继承另外一个类。一般称继承的类为子类,被继承的类称为父类;子类会自动继承父类所有的方法和属性;
(2)为什么使用继承?
当我们定义一个类时,发现另一个类的功能这个类都需要,而这个类又要增加一些新功能时,就可以使用extends关键字来继承那个类,这样可以实现代码的复用性;
Java中子类不能获得父类的构造器。
(3)继承的特点:
a) Java中只支持单继承,不支持多继承,但是可以多重继承。这是因为如果一个类继承了多个类,子类调用该方法时便不知道该调用哪一个类中的方法了;
b) 子类中可以使用super关键字调用父类成员,super是调用该方法的对象的父类对象;
(4)向上转型:
可以定义一个父类类型的变量来记住子类对象,这个在程序中被称为向上转型。
把一个子类当作父类来使用是可以的,因为父类有的子类都有,但是把一个父类当作子类来使用就是不对的,因为子类有的父类不一定能有!
(5)强制类型转换:
把一个子类当作父类来用的时候,因为不能调用子类的特有方法;这种情况下,就需要强制类型转换,将父类类型强制转换成子类类型,以“(子类名)变量名”形式进行强制类型转换。
(6)覆盖(Override):覆盖方法必须和被覆盖方法具有相同的方法名称、参数列表和返回值类型。子类的方法返回值类型可以是父类方法返回值类型的子类。
与重载(Overload)的区别:重载和覆盖
多态(Polymorphism)
(1)定义:在面向对象语言中,可以将函数的形参定义为一个父类类型,而真正调用该函数时这个父类类型的所有子类对象都可以传入,根据传入的子类对象不同函数可以运行出多种形态。
(2)特点:
a) 应用程序不必为每一个子类编写功能调用,只需要对抽象基类进行处理即可,这样提高了程序的复用性;
b) 子类的功能可以被基类的引用变量引用,这叫向后兼容,提高程序的可扩充性和可维护性。
c) 虽然可以预先使用,但是只能访问父类中已有的功能,鱼腥的是后期子类的功能内容,不能预先使用子类中定义的特有功能。
(3)多态中调用成员注意事项:
Fu f = new Zi();
a) 成员变量:编译看左边,运行看左边;
b) 成员方法:编译看左边,运行看右边;
c) 静态方法:编译看左边,运行看左边。
构造函数
构造函数的定义
构造函数是一种特殊的函数,其中函数名和类名相同,没有返回值类型,使用return关键字返回任何值,并且在使用new关键字创建对象直呼自动调用。
构造函数的重载
构造函数的重载和普通函数相同,函数名相同,参数列表不同即可。
构造函数的使用
构造函数在new关键字创建对象时调用;也可以在该类其它构造函数的第一个语句使用this关键字调用;
每一个类都有构造函数,即使没有显式定义构造函数,也会生成一个默认无参的构造函数,其中没有任何内容。
在定义构造函数时,如没有特殊需要,应使用public关键字修饰构造函数;在一些特定情况下,如果不想让他人调用该对象,可以使用private修饰构造函数,比如单例模式。
关键字
this关键字
在构造函数中调用其他构造函数以外,还可以当作一个引用使用,用于方法中,哪个对象调用方法,this就引用哪个对象。有以下三种情况:
(1)区别方法中局部变量和成员变量重名,调用成员变量时可以使用“this.成员变量”形式访问成员变量;
(2)在方法中要将调用该方法的对象作为参数传递给另一个方法时,可以使用“this”作为实参传给该方法;
(3)在内部类中访问外部类的成员时,可以使用“外部类名.this.成员名”形式访问。
static关键字
Static关键字用来修饰类的成员,被这个关键字修饰的成员都和类加载有关;
JVM运行时不会将所有类加载到内存,因为无法确定程序中要使用哪些,类在第一次使用时加载,而且只加载一次。
(1)静态变量:使用static修饰的变量;是在类加在后就初始化,并且被类中的所有实例所共享;可以使用“类名.变量名”形式访问静态变量;
如果在定义一个类的时候,发现一个成员变量需要被所有实例所共享,那么该成员变量就需要定义为static的。
(2)静态方法:使用static修饰的方法;是在类加载后就可以使用,可以使用“类名.方法名”形式访问;
静态方法不可以直接访问外部非静态成员;因为外部非静态成员必须在类创建对象孩子后才可以使用,而静态方法可以在没有创建对象时就可以使用。
静态方法内部访问外部非静态成员,需要先创建该类对象,通过对象访问;
在静态方法中不可以使用this关键字,因为this是个引用,那个对象调用方法就引用哪个对象,而静态方法是不能被对象引用的,this便无从引用;
如果一个方法不用访问对象的非静态成员,那就可以定义成静态的,便不需要创建对象,直接使用类名调用。
(3)静态代码块:使用static修饰的代码块;在类加载后执行;和静态方法相同,不能使用外部非静态成员;
静态代码块执行和静态变量的初始化顺序由代码从上到下顺序决定;
(4)静态内部类:使用static修饰的类。
详细参阅静态内部类介绍。
final关键字
Final标记的类不能被继承;修饰的方法不能被子类重写;修饰的变量为常量,只能赋值一次;
使用public static final共同修饰的常量就是全局常量,通常全部字母都大写。
函数的参数传递
(1)基本数据类型的变量作为实参传入函数之后,在函数中将形参改变,调用处的实参不变。
因为基本数据类型的值是直接存储在变量中,传入函数之后函数中的形参也同样存在了一个值,而这两个值是没有联系的,所以函数中将形参改变时修改的只是函数中变量的值。
(2)引用数据类型的变量作为实参传入函数之后,在函数中将形参改变,调用出的实参改变。
因为引用数据类型变量中存储的是地址,传入函数之后函数中的形参存储的样式同样一个地址,函数中将这个形参改变是改变的都是同一个地址上的对象,所以一处改变的时候两边都改变。
垃圾回收
对象在没有任何引用可以到达时,生命周期结束,称为垃圾。
详细参阅对象的生命周期。
设计模式
设计模式是指解决某类问题行之有效的方法,是一种思想、一种规律的总结。
单态设计模式(SingletonPattern)
(1)定义:保证在整个程序中某个类只能存在一个对象,这个对象不能再创建第二个对象。
(2)步骤:先将构造函数私有化,这样便可禁止其它程序建立该类对象;然后在本类中创建一个该类私有对象;最后为了方便其它程序访问到该类对象,对外提供一个公共访问方式。
Runtime类便是单例设计模式。
(3)两种方式:
i. 饿汉式:当类加载时候,就创建对象;示例代码如下所示:
class Student
{
//构造函数私有化
private Student(){}
//创建该类私有对象
private static final Student s = new Student();
//对外提供一个公共访问方式
public static Student getInstance()
{
return s;
}
}
ii. 懒汉式:当使用的时候才去创建对象;示例代码如下所示:
class Student
{
//构造函数私有化
private Student(){}
//创建该类私有对象,并赋值为null
private static final Student s = null;
//对外提供一个公共访问方式
public static Student getInstance()
{
if (s == null)
{
//当使用的时候,才创建对象
s = new Student();
}
return s;
}
}
iii. 饿汉式与懒汉式的区别:
饿汉式:类一加载进内存就创建好了对象;开发时常使用此模式,因为简单安全;
懒汉式:类加载进内存的时候,对象还没有存在,只是调用了getInstance()方法时,对象才开始创建。属于延迟加载,但是如果多个线程同时操作懒汉式时就可能出现线程安全问题。
组合设计模式(CompositePattern)
(1)定义:当我们在定义一个类的时候需要使用另一个类的方法时,可以使用组合。
定义一个所需要类类型的成员变量,通过构造函数进行装配时没接受一个该类类型的对象,然后使用成员变量引用。
(2)优点:如果两个类没有父子关系,便不适合使用继承;Java中只支持单继承,组合不占用继承位置。
模版设计模式(TemplatePattern)
(1)原因:在解决一些问题时候,需要先定义好一个模版,就相当于一种实现定义好的协议,之后要做一系列的事情都是按照这个模版来做,这样便于实现统一化管理。
(2)如何实现?
a) 定义一个抽象的父类作为模版,定义所有需要的方法;
b) 在父类中实现供外界调用的主方法,将方法声明为final;
c) 根据不同的业务需求定义子类实现父类的抽象方法。
对象的比较
equals方法
用于比较两个独立对象的内容是否相同;
在自定义的类中,如果重写equals方法,需要按照需求来写。
双等于
专门用于比较两个变量的值是否相等,也就是用于比较变量所对应的内存中存储的数值是否相同。
如果一个变量指向的数据是对象类型的,那么便对应两块内存,对象本省占用一块内存(堆内存),而变量占用一块内存。对于指向对象类型的变量,如果需要比较两个变量是否指向同一个对象,即考虑这两个变量所对应的内存中的数值是否相等,这便需要“==”操作符进行比较。
例如以下代码:
String a = new String("mavs41");
String b = new String("mavs41");
两条new语句创建了两个对象,然后用a和b两个变量分别指向了其中一个对象,这是两个不同的对象,因为它们的首地址是不同的,即a和b中存储的数值是不同的,所以a==b是false,而这两个对象中的内容是相同的,所以,a.equals(b)将返回true。
抽象类
定义
使用abstract关键字修饰的类就是抽象类,抽象类是不能使用new关键字实例化对象,原因在于抽象类中含有抽象方法,不恩那个被调用;
没有方法体的方法称为抽象方法,使用abstract关键字修饰;
原因
多个类有相同的方法声明,但是方法体不一样;此时可以把方法声明进行抽取,让子类来继承后,自己实现方法体。没有方法体的方法,使用abstract关键字来标志。
特点
(1)抽象类和抽象方法都使用abstract关键字修饰;
(2)抽象类不能被实例化;
(3)有抽象方法的类必须声明为抽象类,抽象类不一定含有抽象方法。
(4)成员变量:抽象类中可以有变量,也可以有常量;
(5)成员方法:抽象类中可以有抽象方法,也可以非抽象方法;
(6)构造方法:抽象类也是一个类,所以有构造方法;虽然本身不能被实例化,但是可以给子类实例化使用。
问题
(1)抽象类中是否有构造方法?能不能被实例化?如果不能,为什么有构造方法?
答:抽象类中有构造方法,但是抽象类不能被实例化,抽象类中的构造方法供子类实例化调用;
(2)抽象关键字abstract不能和哪些关键字共存?
答:private:私有内容子类继承不到,所以不能重写;
Final:修饰的方法不能被重写;
Static:假如一个抽象方法使用static修饰,那么这个方法可以直接通过类名来调用
(3)抽象类中可不可以没有抽象方法?如果可以,这样的类有什么用?
答:抽象类中可以没有抽象方法;只是为了不让别的类建立该抽象类的对象,此在AWT中有所体现。
内部类(InnerClass)
类中的内部类
在类里面定义的类称为内部类,内部类是外部类的一个成员。
内部类必须创建外部类的对象才能使用;通过一个外部类对象才能创建内部类对象,格式如下所示:
外部类名.内部类名 变量名 = new 外部类名().new 内部类名();
内部类可以直接访问外部类的成员,而外部类不能直接访问内部类的成员,访问方式为:
外部类名.this.成员名
内部类可以直接访问外部类成员,因为在使用内部类时一定会有外部类对象,且只对应一个;而外部类不能直接访问内部类成员是因为在使用外部类时有可能还没有创建内部类对象;
内部类的class文件名为:外部类名.内部类名.class.
方法中的内部类
一个类如果只在某个方法中使用,那么可以在方法中定义。
定义在方法中的类只能在方法中使用,而且使用的代只能在声明的代码下面;即方法中内部类只有在运行到类定义之后才能使用。
方法中定义的内部类不能访问方法中定义的局部变量,除非这个局部变量被声明为final类型。
方法中内部类class文件名为:外部内明$.编号内部类名.class
匿名内部类
是局部内部类的简写,前提是继承一个类或者实现一个接口;
其语法格式如下所示:
new 父类名或者接口名()
{
重写父类方法或者实现接口中的方法;
也可以自定义其它方法;
};
匿名内部类的优缺点:
优点:简化了代码的书写;
缺点:不能直接调用自己的特有方法;
不能执行强转动作;
如果该类里面的方法较多,不允许使用匿名内部类,一般不超过三个。
静态内部类
可以使用static修饰一个类中的内部类;静态内部类不用创建外部类对象就可以直接创建对象,通过以下方式:
外部类名.内部类名 变量名 = new 外部类名.内部类名();
在静态内部类中可以定义静态成员,因为静态内部类可以直接使用,无需创建外部类对象;而静态内部类不能访问外部非静态成员,是因为创建静态内部类是不需要外部类对象,也就是有可能没有创建外部类对象,使用外部类成员必须有外部类对象。
接口
定义
当一个类中的方法都是抽象的时候,Java语言提供了另一种表示方式叫接口,用interface关键字来表示,类与接口之间关系用implements表示。语法定义如下所示:
[修饰符] interface 接口名 extends 父接口1,父接口2...
{
零到多个常量定义...
零到多个抽象方法定义...
}
用法
(1)定义一个类来实现接口,使用implements关键字;
(2)实现一个接口需要实现接口中的所有方法,但是抽象类除外;
(3)通常使用匿名内部类实现一个接口;
(4)接口可以继承接口,使用extends关键字,但是接口不能继承抽象类,因为抽象类中可能有不抽象的方法;
(5)一个类可以实现多个接口,目的是为了实现多态。
方法和变量
(1)成员方法:都是抽象的,默认修饰符是public abstract;
(2)成员变量:是常量,默认修饰符是public static final;
(3)类与类的关系:是继承关系,类与类只能是单继承,可以是多重继承;
类与接口的关系:是实现关系,类可以实现接口;而且类在继承一个类的同时可以实现多个接口;
接口与接口的关系:是继承关系,接口可以多继承接口。
与抽象类的比较
(1)相同点:
a) 都不能被实例化,它们都位于继承树的顶端,用于被其它类实现和继承;
b) 都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现其抽象方法。
(2)不同点:
a) 抽象类只能被单继承,而接口可以多实现,接口的出现避免了多继承的局限性;
b) 抽象类中的数据特点:
成员变量:可以是变量,也可以是常量;
成员方法:可以是抽象方法,也可以是非抽象方法;
构造方法:有构造方法;
接口中数据特点:
成员变量:是常量,默认修饰符是public static final;
成员方法:是抽象方法,默认修饰符是public abstract;
构造方法:没有构造方法。
c) 抽象类中定义的是继承体系中的共性功能;而接口中定义的是继承体系中的扩展功能;
d) 抽象类被继承是“is a”关系:xx是yy的一种;接口被实现是“like a”关系:xx像yy的一种。
异常
定义以及Throwable
定义:指Java程序在运行过程中出现的错误;
Throwable:是Java中可抛出的对象,它是所有错误和异常的父类;关系图如下所示:
其中:Error表示错误,Exception表示异常,RuntimException表示运行时异常,是Exception的子类。
分类
(1)Error(错误):由Java虚拟机生成并抛出,包括动态链接失败,虚拟机错误等,程序不会对其处理;
(2)Exception(异常):所有异常类的父类,子类可以定义各种异常事件,需要用户显式的声明向外抛出或捕获;
(3)RuntimException(运行时异常):在编译时,不需要处理,编译器不检查;该异常发生时,建议不处理,让程序停止,修正代码;
(4)编译时异常:Exception中除了RuntimException的子类,其他异常都必须要进行处理,如果不处理,编译时会报错。
(5)Exception和RuntimException的区别:
a) Exception:一般性的错误,需要编写代码进行处理;
b) RuntimException:运行时异常,不需要处理;
c) 在用throws抛出一个异常的时候,如果这个异常是RuntimException以及RuntimException的子类时候,我们在调用的地方可以不处理;
如果这个异常属于Exception体系的时候,我们在调用的地方必须进行处理或者继续抛出。
用法
(1)处理异常:
a) 在程序中可以在方法后面使用throws关键字声明向外抛出异常;
b) 对于编译时异常,通常需要使用try...catch语句进行捕获,finally可以结合try...catch使用,出现异常,finally里面的代码也会执行,语法格式如下所示:
try{需要被检测的代码;} catch(){处理异常的代码;}finally{一定会执行的代码;}
变形格式:
try{需要被检测的代码;} catch(){处理异常的代码;}
try{需要被检测的代码;} catch(){处理异常的代码;}catch(){处理异常的代码;}....
try{需要被检测的代码;}
catch(){处理异常的代码;}
catch(){处理异常的代码;}....
finally{一定会执行的代码;}
多个异常同时捕获的时候,必须是先捕获小异常,然后才能捕获大异常,Exception必须放在最后面捕获。
Finally永远都会被执行,除非使用System.exit(0)退出JVM;
(2)异常中的一些细节:
a) Final、finally、finalize三者的区别:
i. Final是最终的意思,它可以用于修饰类,成员变量,成员方法;它修饰的类不能被继承,它修饰的变量是常量,它修饰的方法不能被重写;
ii. Finally:是异常处理里面的关键字;它其中的代码永远都会执行,除非使用System.exit(0)退出JVM;
iii. Finalize:是Object类中的一个方法,它是垃圾回收器的一种方式。
b)假如catch中有return语句,finally里面的代码还会执行不?是在return之前还是之后?
答:会执行,会在return之前执行finally里面的代码。
(3)自定义异常:自定义的类继承Exception或者RuntimException
目的:为了让该自定义类具备可抛性,让该类具备操作异常的共性方法。
示例代码如下所示:
class MyException extends Exception/RuntimException
{
MyException(){}
MyException(String message)
{
super(message);
}
}
异常的注意事项:
在子父类覆盖时:
子类抛出的异常必须是父类异常的子类或者子集;
如果父类或者接口没有异常抛出时,子类覆盖出现异常,只能try,不能抛。
(4)throws和throw的区别?
a) 有throws的时候可以没有throw,但有throw的时候,如果throw抛出的异常是Exception体系,那么必须有throws在方法上声明;
b) Throws用于方法上的声明上,气候跟的是异常类名,后面也可以跟多个异常类,之间使用逗号隔开;
Throw用于方法体中,其后跟的是一个异常对象名。
包以及访问控制符
Java中常用的包
定义方式
使用package关键字加上包名来定义类所属于的包,包名全部小写;格式如下所示:
package packageName;
说明:(1)package语句放在Java源文件中的第一条语句;
(2)如果一个类中没有使用Package语句,说明这个类为缺省无包名;
(3)一个类如果想被其他包中的类所引用,必须使用public关键字修饰;构造函数也需要public。
使用方式
(1)在使用带包的类名时需要使用全限定名(包名.类名);
(2)使用import导入包,分为两种
i. 导入单个包,语法格式如下所示:
import package.subpackage...ClassName;
ii. 导入所有包,语法格式如下所示:
import package.subpackage...*;
编译运行
编译一个带包的源文件,在生成class文件的同时需要生成包文件,编译命令如下所示:
javac -d <目录> 源文件名.java
运行有包的类时需要加上包名,运行命令如下所示:
java 包名.类名
访问控制符
一般格式:
成员变量:权限修饰符 + static/final + 数据类型 + 成员变量名
成员方法:权限修饰符 + static/final/abstract + 返回类型 + 方法名
jar文件
定义
Jar文件是一种Java文件的压缩格式,一般来讲,我们会将一个软件系统中的所有class文件使用命令压缩成一个jar文件供其他人使用;
当我们用到jar包中的类时,需要将jar的绝对路径添加到classpath中。
如何压缩
打 jar 的命令:
jar cvf 包名.jar 要打包的文件/文件夹
运行jar文件命令:
java -jar 文件名.jar
查看jar包命令:
jar -tvf 包名.jar [>定向文件]
解压缩命令:
jar -xvf 包名.jar