第三章面向对象(上)
3.1 面向对象的思想(特点)
- 封装性:(核心思想)把对象的属性和行为看成一个整体,将两者封装在一起;隐藏信息
- 继承性:描述类之间关系。通过继承,得以在无需编写原有类的情况下,对原有类功能扩展
- 多态性:在一个类中定义的属性和方法被其他类继承后,它们可以具有不同的数据类型或表现出不同的行为,让同一个属性和方法在不同的类中又不同的语义
3.2类与对象:
类中,一个公共类,多个普通类
对象---抽象---类
类---实例化---对象
3.2.1类的定义:
在面向对象的思想中最核心的就是对象,而创建对象的前提
需要定义一个类,类是Java 的据类,也是组成Java程序的基本要素,所有的 Java 程都是基于类的。
类是对象的抽象,用于描述一组对象的共同特征和行为。类中可以定义成员变量和成员方法,其中,成量用于描述对象的特征,成员变量也被称为对象的属性;成员方法用于描述对象的行为,可简称为方法。
class 类名{
成员变量;
成员方法;
}
根据上述格式定义一个学生类,成员变量包括姓名(name)、年龄(age)、读书read()。学生类定义的示例代码如下:
class Student {
String namei //定义String类型的变量name
int age; //定义int类型的变量age
String sexi //定义String类型的变量sex
// 定义 read()方法 void read(){
System.out.println("大家好,我是"+name +",我在看书!");
}
}
上述代码中定义了一个学生类。其中,Student 是类名,name、age、sex是成员变量,read()是成员方法,在read()中可以直接访问成员变量name。
脚下留心:局部变量与成员变量的不同
在 Java. 中,定义在类中的变量称为成员变量,定义在方法中的变量称为局部变量。如果在某一个中定义的局部变量与成员变量同名,这种情况是允许的,此时,在方法中通过变量名访问到的是局部变而并非成员变量,请阅读下面的示例代码:
class Student {
int age - 30; //类中定义的变量称为成员变量
void read (){
int age=50; // 方法内部定义的变量称为局部变量
system.out.println("大家好,我”+age+"岁了,我在看书!");
上述代码中,在Student 类的 read()方法中有一条打印语句,访问了变量age,此时访问的是局部变 age,也就是说当有另外一个程序调用read()方法时,输出的 age 值为 50,而不是 30。
3.2.2对象的创建与使用
在3.2.1节中定义了一个Student 类,要想使用一个类则必须要有对象。在Java程序中可以使用new建字创建对象,具体格式如下:
类名对象名称=null;
对象名称=new类名();
上述格式中,创建对象分为声明对象和实例化对象两步,也可以直接通过下面的方式创建对象,具体式如下:
类名对象名称=new类名();
例如,创建Student类的实例对象,示例代码如下:
Student stu=new Student ();
上述代码中,new Student用于创建Student类的一个实例对象,Student stu则是声明了一个Student 类型的变量stu。运算符“=”将新创建的Studert 对象地址赋值给变量stu,变量stu引用的对象简称为 stu对了解了对象的创建之后,就可以使用类创建对象了,示例代码如下:
class Student {
String name;
// 声明姓名属性
void read ()l
SystemoutprintIn("大家好,我是"+name +"我在看书!");
}
}
public class Test (
public static void main (Stringll args[]){
Student stu = new Student();
}
}
//创建并实例化对象
上述代码在main()方法中实例化了一个Sudent对象,对象名称为stu。使用 new关键字创建的对象在堆内存分配空间。
创建Sudent对象后,可以使用对象访问类中的某个属性或方法,对象属性和方法的访问通过"."运算符实现,具体格式如下:
对象名称.属性名
对象名称.方法名
3.2.3对象的引用传递
类属于引用数据类型,引用数据类型就是指内存空间可以同时被多个栈内存引用。
下面通过一个案例大家详细讲解对象的引用传递,
class Student
// 声明姓名属性
String name;
//声明年龄属性
int agei
void read()(
6 system.out.println("大家好,我是"+name+",年龄"+age);
}
}
class Example02{
Student stul-new StudentO; public static void main (String[l args)(//声明stul对象并实例化
stu2= stul; Student stu2=null; //声明stu2对象,但不对其进行实例化
//stul给stu2分配空间使用权
stul.name=“小明”; //为stul对象的name属性赋值
stul.age=20;
15 stu2.age=50;
16
17 stu2.read((); stul.read(); // 调用对象的方法
}
}
提示:
一个栈内存空间只能指向一个堆内存空间,如果想要再指向其他堆内存空间,就必须先断开已有的指向后才能再分配新的指向。
3.2.4 访问控制
针对类、成员方法和属性,Java提供了4种访问控制权限,分别是private、default、protected 和public。将这4种访问控制权限按级别由小到大依次列出,如下:
private---default---protected---public
4种访问控制权限,具体介绍如下。
(1)private:private属于私有访问权限,用于修饰类的属性和方法。类的成员一旦使用了private关键字修饰,则该成员只能在本类中进行访问。
(2)default:default属于默认访问权限。如果一个类中的属性或方法没有任何的访问权限声明,则该属性或方法就是默认的访问权限,默认的访问权限可以被本包中的其他类访问,但是不能被其他包的类访问。
(3)protected:属于受保护的访问权限。一个类中的成员使用了protected访问权限,则只能被本包及不同包的子类访问。
(4)public:public属于公共访问权限。如果一个类中的成员使用了public访问权限,则该成员可以在所有类中被访问,不管是否在同一包中。
注意:
类名Test只能使用public修饰或者不写修饰符。局部成员是没有访问权限控制的,因为局部成员只在其所在的作用域内起作用,不可能被其它类访问到,如果在程序中这样编写代码,编译器会报错。
3.3封装性
3.3.1为什么要封装
在Java面向对象的思想中,封装可以被认为是一个保护展障,防止本类的代码和数据被外部程序
访问。下面 一个例子 讲解什么是封装
class Student{
String name; // 声明姓名属性
int age;//声明年龄属性
void read(){
System.out.printin("大家好,我是"+name+",年龄"+age);
}
}
public class Example03 {
public static void main (String[] args){
Student stu = new Student();//创建学生对象
stu.name = "张三";
stu.age = -18; 为对象的name 属性赋值
stu.read();
}
}
在以上代码中是不会有任何问题的,因为int的值可以取负数。但是在现实中,-18不理的。避免这种错误的发生,在设计 Student 类时,应该对成员变量的访问做出一些限定,不允许外界随意访问,这就需要实现类的封装。
3.3.2 如何实现封装
类的封装是指将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象的内部信息,而是通该类提供的方法实现对内部信息的操作访问。
private关键字修饰类的属性,被私有化的属性只能在类中被访问。如果外界想要访问私有属性,则必须通过setter和getter方法设置和获属性值。
下面使用private关键字修饰 name 属性和 age属性,实现类的封装
class Student{
private String name;// 声明姓名属性
private int age; // 声明年龄属性
public String getName (){
return name;
}
public void setName (String name){
this.name = name;
}
public int getAge(){
return age;
public void setAge (int age){
if(age<=0){
System.out.println("您输入的年龄有误!");
}else{
this.age = age;
}
}
public void read(){
System.out.println("大家好,我是"+name+",年龄"+age);
}
}
public class Example04 {
public static void main (String[] args){
Student stu = new Student (); // 创建学生对象
stu.setName("张三"); //为对象的name属性赋俏
atu.setAge(-18); //为对象的age 属性赋值
stu.read (); //调用对象的方法
}
}
使用private关键字将属性name和age 声明为私有变量,并对外界提供公有的访问方法,其中,getName()方法和getAge()方法用于获取name属性和age属性的值,setName()方法和setAge()方法用于设置name 属性和 age 属性
的值。
3.4构造方法
3.4.1.定义构造方法
(1)构造方法的名称必须与类名一致;
(2)构造方法名称前不能有任何返回值类型的声明;
(3)不能在构造方法中使用return返回一个值,但是可以单独写return语句作为方法的结束。
下面代码演示构造方法的定义:
class Student{
public Student () {
System.out.println("调用了无参构造方法");
}
}
public class Example05 {
public static void main (String[] args){
System.out.println("声明对象...");
Student stu=null; //声明对象
System.out.println("实例化对象'..");
stu=new Student((); //实例化对象
}
}
上面是有参构造方法
下面是无参构造方法
class Student{
private String name;
private int age;
public Student (String n, int a){
name=n;
age = a;}
public void read () {
System.out.println("我是:"+name+",年龄:"+age);
}
}
public class Example06 {
public static void main (String[] args){
Student stu = new Student("张三",18); // 实例化Student对象
stu.read();
}
}
代码(1)(2)运行结果如下
3.4.2 构造方法的重载
与普通方法一样, 构造方法也可以重载,在一个类中可以定义多个构造方法,只要每个构造方法的参数类型或参数个数不同即可。在创建对象时,可以通过调用不同的构造方法为不同的属性赋值。
class Student{
private string name; private int age;
public Student(){ }
public Student(String n){
name=n;
}
public Student (String n, int a){
name=n;
age=a;
}
public void read(){
System.out.println("我是:"+name+",年龄:"+age);
}
}
public class Example07 {
public static void main (String[] args){
Student stul=new Student("张三");
Student stu2 = new Student("张三"18); //实例化Student对象
stul.read();
stu2.read();
}
}
3.5this关键字
在Java 开发中,成员变量与部变量重名时,需要使用到this关键字分辨成员变量与局部变量,Java中的this 关键字语法比较灵活,其主要作用有以下3种。
(1)使用this 关键字调用本类中的属性。
(2)使用this关键字调用成员方法。
(3)使用this关键字调用本类的构造方法。
3.5.1.使用this关键字调用本类中的属性如
:this.name=name;
3.5.2.使用this关键字调用成员方法如:
this.成员方法();
3.5.3.使用this关键字调用本类的构造方法如:
this(参数1,参数2...);
应注意以下几点:
1,只能在构造方法中使用this调用其他的构造方法,不能再成员方法中通过this调用其它构造方法。
2,在构造方法中,使用this调用构造方法的语句必须位于第一位,且只能出现一次。
3,不能再一个类的两个构造方法中使用this相互调用。
3.6代码块
是用{}括号括起来的一段代码,根据位置及声明关键字的不同,分为普通代码块,构造块,静态代码块,同步代码块4种。
3.6.1.普通代码块
普通代码块:直接在方法或者语句中定义的代码块。
3.6.2.构造块
构造块又称为构造代码块:直接在类中定义的代码块。
3.7static关键字
在定义一个类时,只是在描述某事物的特征和行为,并没有产生具体的数据。只有通过new关键字该类的实例对象时,才会开辟栈内存和堆内存,在堆内存中每个对象会有自己的属性。如果希望某些属所有对象共享,就必须将其声明为static属性。如果属性使用了static关键字进行修饰,则该属性可以直用类名称进行调用。除了修饰属性,static 关键字还可以修饰成员方法。
static关键字static可访问变量方法,不访问构造方法。this可以访问变量方法,构造方法。某些属性被所有对象共享,必须将其声明为static属性。static可以修饰属性和成员方法,不改变局部变量。
3.7.1.静态属性
java中使用static修饰属性,该属性成为静态属性(也成全局属性),静态属性可以用类名直接访问。格式:
类名.属性名
static关键字只能修饰成员变量,不能修饰局部变量。
3.7.2.静态方法
使用static关键字修饰的方法通常称为静态方法。同静态变量一样,静态方法也可以通过类名和对象访问,如下:
类名.方法
或
实例对象名.方法
下面一个案例,学习静态方法的使用
cIass student {
private String name; // 定义name属性
private int age; //定义age属性
private static String school="A大学"; //定义school属性
public Student (String name,int age){
this.name mname;
this.age=age;
}
public void info(){
system.out.println("姓名:"+this.name+",年龄:"+ this.age+",学校:" + school);
}
public string getName(){
return name;
}
public void setName (String name)(
this.name =name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.agem age;
}
public static String getSchool(){
return school;
}
public static void setSchool (String school){
Student.school-school;
}
}
class Example15{
public static void main (String[] args){
Student stul=new Student(“张三",18); // 创建学生对象
student stu2= new Student("李四"19);
Student stu3=new Student("王五"20);
stul.setAge(20);
stu2.setName("小明");
Student.setSchool("B大学");
stul.info();
stu2.info();
stu3.info();
}
}
效果图如下:
Student类将所有的属性都进行了封装,所以想要更改属性就必须使用 setter 方法。 代码声明了name、age和school属性的 getter和setter方法, 代码分别对name、age和 laisned,school属性的值进行修改,但是school 属性是使用 static声明的,所以可以直接使用类名 Student 进行调用。
注意:
静态方法只能访问静态成员,因为非静态成员需要先创建对象才能访问,即随着对象的创建,非静态成员才会被分配内存。而静态方法在被调用时可以不创建任何对象。
3.7.3 静态代码块
static关键字修饰的代码块称为静态代码块。类只加载一次,静态代码块只执行一次。在第一次使用时才会被加载,并且只会加载一次。
第四章面向对象(下)
4.1类的继承
4.1.1.继承的概念
类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类称为父类。子类继承父类的属性和方法,使得子类对象就有父类的特征和行为。子类也可以定义自己的属性和方法。
声明一个类继承另一个类,要使用关键字extendsclass父类{}class子类extends父类{}
类的继承中,需要注意一些问题:
1,类只支持单继承,不允许多继承。一个类只能有一个父类;
2,多个类可以继承一个父类;
3,多层继承是可以的,一个类的父类可以再继承另外的父类;
4,子类和父类是一种相对概念,一个类可以是某个类的父类,也可以是另一个类的子类。
在继承中,子类不能直接访问父类中的私有成员,子类可以调用父类的非私有方法,但不能调用父类的私有成员。
4.1.2.方法的重写
继承关系中,子类会自动继承父类中定义的方法,但有时在子类中要对继承的方法进行一些修改,即对父类方法进行重写。在子类中重写的方法需要和父类重写的方法具有相同的方法名,参数列表和返回值类型,且在子类中重写的方法不能拥有比父类方法更加严格的访问权限。
4.1.3.super关键字
super关键字可以在子类中调用父类的普通属性,方法和构造方法。
super关键字的具体用法:
1,使用super关键字访问父类的成员变量和成员方法。
具体格式:
super.成员变量
super.成员方法(参数1,参数2...)
2,访问父类中指定的构造方法。
super(参数1,参数2...)
4.2final关键字
final声明变量时,全部的字母大写,程序中的变量使用publicstaticfinal声明,则此变量将成为全局变量。final关键字可以声明类,属性,方法。声明时需注意几点:
(1)final修饰的类不能有子类;
(2)修饰的方法不能被子类重写;
(3)修饰的量(成员变量和局部变量)是常量,常量不可修改。
4.2.1.final关键字修饰类
类被final修饰后,该类不可被继承,即不能派生子类。
4.2.2.final关键字修饰方法
类的方法被final修饰后,该类的子类将不能重写该方法。
4.2.3.final关键字修饰变量
修饰的变量为常量,常量只能在声明时被赋值一次。
4.3.抽象类和接口
4.3.1抽象类
特点:无方法体,没有执行代码
抽象类方法的重写@override覆盖抽象类和抽象方法一样必须使用关键字abstract抽象方法在定义时不需要实现方法体,使用abstract修饰成员方法abstract返回值类型方法名称(参数);
抽象类的定义规则:
1,包含抽象方法的类必须是抽象类;
2,抽象类和抽象方法都要使用abstract关键字声明;
3,抽象方法只需声明而不需要实现;
4,如果一个非抽象类继承了抽象类,那么该子类必须实现抽象类中的全部抽象方法。
注意:
使用abstract关键字修饮饰的抽象方法不能使用private修各饰,因为抽象方法必须被子类实现,如果使用了 private 声明,则子类无法实现该方法。
4.3.2 接口
接口---抽象类---类
如果一个抽象类中所以的方法都是抽象的,则可以将这个类定义接口。接口除包括抽象方法,还有默认方法(default修饰)和静态方法(interface修饰),且这两种方法都允许有方法体。接口使interface关键字声明,语法格式如下:
public in terface 接口名 extends 接口1,接口2...{
public static final 数据类型常量量名=常量值;
publi cabstract 返回值类型抽象方法名称(参数列表);
}
接口1extends接口2一个接口可以有多个父接口,父接口之间用逗号分隔。目的:克服单继承的限制,因为一个类只能有一个父类,而一个接口可以同时继承多个父接口。
注意:
不管写不写访问权限,接口中方法的访问权限永远是public。
接口包含三类方法:抽象,默认,静态方法。
类到接口使用implements
修饰符class类名implements接口1,接口2,...{
...
}
4.4多态
多态必要条件:
1,继承子类父类中的方法;
2,重写方法的重写......表现不同的行为;
4.4.1.多态的概述
多态指不同对象在调用同一个方法时表现出的多种不同行为。在同一方法中自由与参数类型不同而导致执行效果不同的现象就是多态。
多态主要有两种形式:
1,方法的重载;
2,对象的多态性(方法的重写)。
4.4.2.对象类型的转换
1,向上转型:子类对象---父类对象;
2,向下转型:父类对象---子类对象。
向上转型,程序会自动完成;向下转型,必须指明要转型的子类类型。
对象向上转型:
父类类型父类对象=子类实例;
对象向下转型:
父类类型 父类对象=子类实例;
子类类型 子类对象=(子类)父类对象;
注意:进行向下转型前,必须发生向上转型,否则将出现对象准换异常。在进行向下转型时不能直接将父类实例强制转化为子类实例,否则程序会报错。
4.4.3.instanceof关键字
判断一个对象是否是某个类或接口的实例,语法格式:
对象instanceof类(或接口)
上述格式中,如果对象是指类的实例对象,则返回true,否则返回false。
4.5object类
所有类的父类,每个类都直接或间接继承object类。没有使用extends关键字为这个类显示指定父类,那么该类会默认继承object类。
object类常用方法:
booleanequals()判断两个对象是否相等;
inthashCode()返回对象的散列码值;
StringtoString()返回对象的字符串表示形式
4.6内部类
4.6.1.成员内部类
一个类中除了可以定义成员变量,成员方法外,还可以定义类,这样的类称为成员内部类。
外部类名.内部类名变量名= new外部类名.new内部类名();
4.6.2.局部内部类
局部内部类也称方法内部类,指定义在某个局部范围中的类,它与局部变量一样,都是在方法中定义的,有效范围只限于方法内部。
4.6.3.静态内部类
静态内部类使用static关键字修饰的成员内部类。语法格式:
外部类名.静态内部类明变量名=new外部类名().静态内部类名();
4.6.4匿名内部类
匿名内部类没有名称的内部类。如果该方法的参数是接口类型,除了可以传入一个接口实现类外,还可以使用实现接口的匿名内部类作为参数,在匿名内部中直接完成方法的实现。基本语法格式:
new父接口(){
//匿名内部类
}
4.7异常
4.7.1什么是异常
异常处理的好处:增加程序的健壮性。Error类称为错误类,表示java程序运行时产生的系统内部错误或资源耗尽的错误。Exception类称为异常类,表示程序本身可以处理的错误java程序进行的异常处理,都是针对Exception类即其子类。
Throwable类中的常用方法:
1,StringgetMessage()返回异常的消息字符串
2,StringtoString()返回异常的简单信息描述
3,voidprintStackTrace()获取异常类名和异常信息,以及异常出现在程序中的位置,把信息输出在控制台。
4.7.2.Try...catch和finally
异常捕获使用try...catch实现基本语法格式:
try{
//程序代码块
}catch(ExceptionType(Exception类及其子类)e){
}
4.7.3.throws关键字
抛出异常语法格式:
修饰符返回值类型方法名(参数1,参数2...)throws异常类1,异常类2......{
//方法体...
}
4.7.4编译时异常与运行时异常
1.编译时异常
Exception类中,除了RuntimeException类及其子类外,Exception的其他子类都是编译时异常。
两种方式处理编译时异常:
(1)使用try...catch语句对异常进行捕获处理;
(2)使用throws关键字声明抛出异常,调用者对异常进行处理。
2.运行时异常
RuntimeException类及其子类都是运行时异常。
特点:java编译器不会对异常进行检查。逻辑错误。
4.7.5自定义异常
自定义的异常类必须继承自Exception或其子类。在构造方法中使用super()语句调用Exception的构造方法即可。自定义异常的具体代码如下:
//下面的代码是自定义一个异常类继承自Exception
public class DivideByMinusException extends Exc eption{
public DivideByMinusException (){
super(); //调用Exception无参 的构造方法
}
public DivideByMinusException (String messag ge){
super(message); //调用Exception有参的构造方法
}
}
自定义异常类中使用throw关键字在方法中声明异常的实例对象,语法格式如下:
throw Exception异常对象