目录
(1)通过this关键字调用成员变量,解决与局部变量名称冲突问题。
(3)final修饰的变量(成员变量和局部变量)是常量,只能赋值一次
一 面向对象
1 什么是面向对象
面向对象是一种符合人类思维习惯的编程思想。现实生活中存在各种形态不同的事物,这些事物之间存在着各种各样的联系。在程序中使用对象来映射现实中的事物,使用对象的关系来描述事物之间的联系,这种思想就是面向对象。
(1)面向过程
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一一实现,使用的时候依次调用就可以了。
(2)面向对象
面向对象则是把构成问题的事务按照一定规则划分为多个独立的对象,然后通过调用对象的方法来解决问题。
2 面向对象的特点
封装:
封装是面向对象的核心思想,将对象的属性和行为封装起来,不需要让外界知道具体实现细节。
继承:
继承主要描述的就是类与类之间的关系,通过继承,可以在无需重新编写原有类的情况下,对原有类的功能进行扩展。
多态:
多态指的是在一个类中定义的属性和功能被其他类继承后,当把子类对象直接赋值给父类引用变量时,相同引用类型的变量调用同一个方法所呈现出的多种不同行为特性。
3 类和对象
说明:面向对象的编程思想,力图让程序中对事物的描述与该事物在现实中的形态保持一致。为了做到这一点,面向对象的思想中提出了两个概念——类和对象。
抽象概念:类是对某一类事物的抽象描述,而对象用于表示现实中该类事物的个体。
如图:
分析:可以将上图人看作是一个类,将每个具体的人(如小韩、小石等)看作对象,从人与具体个人之间的关系便可以看出类与对象之间的关系。
说明:类用于描述多个对象的共同特征,它是对象的模板,而对象用于描述现实中的个体,它是类的实例。对象是类的具体化,并且一个类可以对应多个对象。
4 类
在面向对象的思想中,最核心的就是对象。
为了在程序中创建对象,首先需要定义一个类。
类是对象的抽象,它用于描述一组对象的共同特征和行为,例如人都有姓名、年龄、性别等特征,还有学习、工作、购物等行为。
以面向对象的编程思想,就可以将某一类中共同的特征和行为封装起来,把共同特征作为类的属性(也叫成员变量),把共同行为作为类的方法(也叫成员方法)。
(1)类的定义格式
[修饰符] class 类名 [extends 父类名] [implements 接口名]{ // 类体,包括类的成员变量和成员方法 }
修饰符:可以是public,也可以不写(默认)
class:定义类关键字
类名:定义类的名字
extends:继承关键字,用于类的继承
implements:实现关键字,用于实现接口
实体:类中编写的内容,主要包括类的成员变量和成员方法
例如:
//定义一个Person类 public class Person{ }
(2)声明(定义)成员变量
[修饰符] 数据类型 变量名 [ = 值];
修饰符:可以是public/private等
数据类型:声明变量存储的数据类型
变量名:变量的名称,必须符合标识符的命名规则
=值:对变量进行赋值,也可以后续再赋值
例如:
public class Person{ private String name;//声明一个String类型的成员变量name private int age = 20;//定义一个int类型的成员变量age,并赋值20 }
(3)声明(定义)成员方法
[修饰符] [返回值类型] 方法名([参数类型 参数名1,参数类型 参数名2,...]){ //方法体 ... return 返回值; //当方法的返回值类型为void时,return及其返回值可以省略 }
例如:
public class Student{ private int age = 10;//类中定义的变量被称作成员变量 public void say(){ int age = 20;//方法内部定义的变量被称作局部变量 System.out.println("age="+age);//访问到的是局部变量:20 } }
注意:
在Java中,定义在类中的变量被称为成员变量,定义在方法中的变量被称为局部变量。
如果在某一个方法中定义的局部变量与成员变量同名,这种情况是允许的,此时方法中通过变量名访问到的是局部变量,而并非成员变量。
5 对象
对象是类的实例
(1)创建对象
类名 对象名称 = new 类名();
例如:
Person p = new Person();
(2)对象内存图
在创建对象时,程序会占用两块内存区域,分别是栈内存和堆内存。
其中Person类型的变量p被存放在栈内存中,它是一个引用,会指向真正的对象;
通过new Person()创建的对象则放在堆内存中,这才是真正的对象。
(3)对象成员的调用
①通过对象的引用来访问对象所有的成员。
对象引用.对象成员
②直接使用创建的对象本身来引用对象成员。
new 类名().对象成员
对比说明:
第2种方式,创建实例对象的同时就访问了对象成员,并且在创建后只能访问其中某一个成员,而不能像第1种方式那样可以访问多个对象成员。
同时,由于没有对象引用的存在,在完成某一个对象成员的访问后,该对象就会变成垃圾对象。
所以,在实际开发中,创建实例对象时多数会使用对象引用。
例如:
//创建对象 Person p = new Person(); //访问对象的属性 int age = p.age; //调用对象的行为 p.say();
(4)成员变量的初始值
成员变量类型 | 初始值 |
---|---|
byte、short、int、long | 0 |
float、double | 0.0 |
char | 一个空字符,即’\u0000’ |
boolean | false |
引用数据类型 | null,表示变量不引用任何对象 |
(5)垃圾对象
形成:当没有任何变量引用对象时,该对象将成为垃圾对象,不能再被使用。
①对象的引用超出作用域。
{ Person p1 = new Person(); ...... }
说明:变量p1引用了一个Person类型的对象,当这段代码运行完毕时,变量p1就会超出其作用域而被销毁,这时Person类型的对象将因为没有被任何变量所引用而变成垃圾。
②对象的引用重新指向空地址(null)。
{ Person p2 = new Person(); ...... p2 = null; ...... }
说明:使用变量p2引用了一个Person类型的对象,接着将变量p2的值置为null,被p2所引用的Person对象就会失去引用,成为垃圾对象。
二 构造方法
1 引入
实例化一个类的对象后,如果要为这个对象中的属性赋值,则必须通过直接访问对象的属性或调用setXxx()方法的方式才可以。
如果需要在实例化对象的同时就为这个对象的属性进行赋值,可以通过构造方法来实现。
2 构造方法的定义
定义:构造方法(也被称为构造器)是类的一个特殊成员,它会在类实例化对象时被自动调用。
格式:
[修饰符] 方法名 ([参数列表]){ // 方法体 }
定义构造方法时需同时满足以下三个条件:
①方法名与类名相同。 |
---|
②在方法名的前面没有返回值类型的声明。 |
③在方法中不能使用return语句返回一个值,但是可以单独写return语句来作为方法的结束。 |
例如:
public class Person { private String name;//使用private修饰成员变量 private int age; public Person(String name,int age){ //给name和age属性进行初始化 this.name = name; this.age = age; } }
创建对象:
Person person = new Person("小明",11);
3 构造方法的重载
定义:与普通方法一样,构造方法也可以重载,在一个类中可以定义多个构造方法,只要每个构造方法的参数类型或参数个数不同即可。
说明:在创建对象时,可以通过调用不同的构造方法来为不同的属性进行赋值。
例如:
public class Person { private String name;//使用private修饰成员变量 private int age; //无参构造函数 public Person(){} //1个参数的构造函数 public Person(String name){ this.name = name; } //2个参数的构造函数 public Person(String name,int age){ //给name和age属性进行初始化 this.name = name; this.age = age; } }
创建对象:
Person p1 = new Person();//调用无参构造函数 Person p2 = new Person("小明");//1个参数构造函数 Person p3 = new Person("小红",11);//2个参数构造函数
注意:
在Java中的每个类都至少有一个构造方法,如果在一个类中没有显示地定义构造方法,系统会自动为这个类创建一个默认的无参构造方法。
class Person{ class Person{ ======> public Person(){}//自动创建无参构造函数 } } //第一种写法,类中没有显示地声明构造方法,但仍然可以用new Person()语句来创建Person类的实例对象。 //系统提供的无参构造方法往往不能满足需求,因此,可以自己在类中定义构造方法,一旦为该类定义了构造方法,系统将不再提供默认的无参构造方法。
4 访问控制符
在Java中,针对类、成员方法和属性提供了四种访问级别,分别是private、default、protected和public。
四种访问控制级别说明:
private(当前类访问级别): | 如果类的成员被private访问控制符来修饰,则这个成员只能被该类的其他成员访问,其他类无法直接访问。类的良好封装就是通过private关键字来实现的。 |
---|---|
default(包访问级别): | 如果一个类或者类的成员不使用任何访问控制符修饰,则称它为默认访问控制级别,这个类或者类的成员只能被本包中的其他类访问。 |
protected(子类访问级别): | 如果一个类的成员被protected访问控制符修饰,那么这个成员既能被同一包下的其他类访问,也能被不同包下该类的子类访问。 |
public(公共访问级别): | 这是一个最宽松的访问控制级别,如果一个类或者类的成员被public访问控制符修饰,那么这个类或者类的成员能被所有的类访问,不管访问类与被访问类是否在同一个包中。 |
通过一个表将这四种访问级别更加直观的表示出来。
访问范围 | private | default | protected | public |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一包中 | √ | √ | √ | |
子类中 | √ | √ | ||
全局范围 | √ |
三 封装
1 为什么需要封装
public static void main(String[] args) { Person p = new Person(); p.name = "张三"; p.age = -18; p.speak(); }
示例分析:上述示例将年龄赋值为一个负数-18,在语法上不会有任何问题,因此程序可以正常运行,但在现实生活中明显是不合理的。
解决方案:为了避免出现上述不合理的问题,在设计一个Java类时,应该对成员变量的访问作出一些限定,不允许外界随意访问,这就需要实现类的封装。
2 如何实现封装
定义:类的封装,是指将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象的内部信息,而是通过该类所提供的方法来实现对内部信息的操作访问。
如何实现封装:在定义一个类时,将类中的属性私有化,即使用private关键字来修饰,私有属性只能在它所在类中被访问,如果外界想要访问私有属性,需要提供一些使用public修饰的公有方法,其中包括用于获取属性值的getXxx()方法和设置属性值的setXxx()方法。
例如:
public class Person { private String name;//使用private修饰成员变量 private int age; public void setName(String name){//提供公共的set方法,用于设置成员变量的值 this.name = name; } public String getName(){//提供公共的get方法,用于获取成员变量的值 return name; } public void setAge(int age){ this.age = age; } public int getAge(){ return age; } }
四 this关键字
1 为什么要使用this关键字
在构造方法中使用的参数是a,成员变量使用的是age,虽然在语法上没有任何问题,但这样的程序可读性很差。这时可以将参数的名字和成员变量名保持一致,但是这样做又会导致成员变量和局部变量的名称冲突,在方法中将无法访问成。
解决方案:为了解决这个问题,Java中提供了一个关键字this来指代当前对象,用于在方法中访问对象的其他成员。
2 this关键字的三种常见用法
(1)通过this关键字调用成员变量,解决与局部变量名称冲突问题。
class Person { int age; // 成员变量age public Person(int age) { // 局部变量age this.age = age; // 将局部变量age的值赋给成员变量age } }
说明:
在上面的代码中,构造方法的参数被定义为age,它是一个局部变量,在类中还定义了一个成员变量,名称也是age。在构造方法中如果使用“age”,则是访问局部变量,但如果使用“this.age”则是访问成员变量。
(2)通过this关键字调用成员方法。
class Person { public void openMouth() { .. } public void speak() { this.openMouth(); } }
说明:在上面的代码中,使用this关键字调用了openMouth()方法。需要注意的是,此处的this关键字可以省略不写,也就是说上面的代码中,写成“this.openMouth()”和“openMouth()”效果是完全一样的。
(3)通过this关键字调用构造方法。
class Person { public Person() { } public Person(int age) { this(); // 调用无参的构造方法 } }
说明:构造方法是在实例化对象时被Java虚拟机自动调用的,在程序中不能像调用其他方法一样去调用构造方法,但可以在一个构造方法中使用“this([参数1,参数2…])”的形式来调用其他的构造方法。
使用this调用类的构造方法时,应注意以下几点:
①只能在构造方法中使用this调用其他的构造方法,不能在成员方法中使用。 |
---|
②在构造方法中,使用this调用构造方法的语句必须是该方法的第一条执行语句,且只能出现一次。 |
③不能在一个类的两个构造方法中使用this互相调用。 |
五 static关键字
1 为什么要使用static关键字
Java中的static关键字,用于修饰类的成员,如成员变量、成员方法以及代码块等,被static修饰的成员具备一些特殊性。比如被static关键字修饰的成员变量、方法可以被类直接访问,而不需要预先构造类的实例化对象。
2 静态变量
在一个Java类中,可以使用static关键字来修饰成员变量,该变量被称作静态变量。
class Student{ String name; int age; static String schoolName = "武汉大学";//静态成员变量 }
注意:
静态变量被所有实例共享,可以使用“类名.变量名”的形式来访问
System.out.println(Student.schoolName);
static关键字只能用于修饰成员变量,不能用于修饰局部变量,否则编译会报错。
public class Student { public void study() { static int num = 10; // 这行代码是非法的,编译会报错 } }
静态变量---内存分配
从图可以看出,所有学生对象共享一个名称为schoolName的变量。在一个Java类中,要实现这种功能可以使用static关键字来修饰成员变量,该变量被称作静态变量,它可以被所有实例所共享。
3 静态方法
被static关键字修饰的方法称为静态方法。
class Student{ String name; int age; public static void say(){ System.out.println("hello world"); } }
同静态变量一样,静态方法可以使用“类名.方法名”的方式来访问,也可以通过类的实例对象来访问。
Student.say();
4 静态代码块
在Java中,使用一对大括号包围起来的若干行代码被称为一个代码块。
//代码块 { ...... ...... }
使用static关键字修饰的代码块称为静态代码块。
//静态代码块 static{ ...... ...... }
当类被加载时,静态代码块会执行,并且只会执行一次。
在程序中,经常使用静态代码块来对类的成员变量进行初始化。
六 类的继承
1 什么是继承
在现实生活中,说到继承,多会想到子女继承父辈的财产、事业等。在程序中,继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关联体系。
例如猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物;同理,波斯猫和巴厘猫继承自猫,而沙皮狗和斑点狗继承自狗。
定义:在Java中,类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类被称作子类,现有类被称作父类或基类,子类会自动拥有父类所有可继承的属性和方法。
2 类继承的语法格式
[修饰符] class 子类名 extends 父类名 { // 程序核心代码 }
例如:
//父类 public class Animal{ public String name; public String color; public void eat(){ System.out.println("吃饭啦"); } } //子类 public class Cat extends Animal{ } // 子类会继承父类的属性的方法 //创建子类对象 Cat cat = new Cat(); //给name属性赋值 cat.name = "喵喵"; //给color属性赋值 cat.color = "白色"; //调用eat方法 cat.eat();
3 类继承需要注意的问题
(1)在Java中,类只支持单继承,不允许多重继承,也就是说一个类只能有一个直接父类。
class A{} class B{} class C extends A,B{} // 报错,C类不可以同时继承A类和B类
(2)在Java中,多个类可以继承同一个父类
class A{} class B extends A{} class C extends A{} // 类B和类C都可以继承类A
(3)在Java中,多层继承是可以的,即一个类的父类可以再去继承另外的父类。
class A{} class B extends A{} // 类B继承类A,类B是类A的子类 class C extends B{} // 类C继承类B,类C是类B的子类,同时也是类A的子类
(4)在Java中,子类和父类是一种相对概念,也就是说,一个类是某个类父类的同时,也可以是另一个类的子类。
例如类B是A的子类,是C的父类
4 重写父类方法
定义:在继承关系中,子类会自动继承父类中公共的方法,但有时在子类中需要对继承的方法进行一些修改,即对父类的方法进行重写。
class Father{ public void eat(){ System.out.println("吃树皮/草根..."); } } class Son extends Father{ //重写父类的eat方法 public void eat(){ System.out.println("吃KFC/金拱门..."); } } //创建Son对象 Son son = new Son(); //调用eat方法 son.eat();//输出"吃KFC/金拱门..."
注意:
①子类中重写的方法需要和父类被重写的方法具有相同的方法名、参数列表以及返回值类型。
②子类重写父类方法时,不能使用比父类中被重写的方法更严格的访问权限。
5 super关键字
问题:在继承关系中,当子类重写父类的方法后,子类对象将无法直接访问父类被重写的方法。
解决方法:在Java中专门提供了一个super关键字来访问父类的成员,例如访问父类的成员变量、成员方法和构造方法。
super关键字的具体使用:
(1)使用super关键字调用父类的成员变量和成员方法
super.成员变量 super.成员方法(参数列表);
例如:
class Fu{ String name = "小明"; int age = 11; public void sleep(){ System.out.println("Fu中的sleep方法"); } } class Zi extends Fu{ String name = "小黑"; int age = 12; public void sleep(){ System.out.println("Zi中的sleep方法"); } public void test(){ //访问父类中的name属性 System.out.println(super.name); //访问父类的age属性 System.out.println(super.age); //调用父类的sleep方法 super.sleep(); } }
(2)使用super关键字调用父类的构造方法。
super(参数列表)
用于给父类的属性进行初始化
例如:
class Fu{ String name; int age; //Fu中的构造函数,给name和age属性初始化 public Fu(String name,int age){ this.name = name; this.age = age; } } class Zi extends Fu{ String sex; //给name,age,sex属性进行初始化 public Zi(String name,String sex,int age){ //调用Fu中的构造函数,给name和age初始化 super(name,age); //给Zi的sex属性初始化 this.sex = sex; } } //创建Zi对象 Zi zi = new Zi("小明","男",11);
6 final关键字
final关键字可用于修饰类、变量和方法,它有“不可更改”或者“最终”的含义。因此被final修饰的类、变量和方法将具有以下特性。
(1)final修饰的类不能被继承;
final class A{} class B extends A{} // 报错,Cannot inherit from final 'A'
(2)final修饰的方法不能被子类重写;
public class A { public final void eat(){ } } public class B extends A{ //报错:'eat()' cannot override 'eat()' in 'com.ycy.entity.A'; overridden method is final public void eat(){ } }
(3)final修饰的变量(成员变量和局部变量)是常量,只能赋值一次
public final void eat(){ final int a = 1; a = 2;//报错:Cannot assign a value to final variable 'a' }
七 Object类
1 什么是Object类
在Java中提供了一个Object类,它是所有类的父类,即每个类都直接或间接继承自该类。
Object类通常被称之为超类、基类或根类。
当定义一个类时,如果没有使用extends关键字为这个类显示地指定父类,那么该类会默认继承Object类。
2 Object类常用方法
方法声明 | 功能描述 |
---|---|
boolean equals(Object obj) | 判断某个对象与此对象是否相等 |
final Class<?> getClass() | 返回此Object的运行时类 |
int hashCode() | 返回该对象的哈希码值 |
String toString() | 返回该对象的字符串表示 |
void finalize() | 垃圾回收器调用此方法来清理没有被任何引用变量所引用对象的资源 |
例如:
public class Student { int xh; String name; int age; public Student(int xh,String name,int age){ this.xh = xh; this.name = name; this.age = age; } //重写equals方法,修改比较规则 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return xh == student.xh;//根据学号判断2个学生是否相同 } //重写equals方法后,一般会重写hashCode方法 @Override public int hashCode() { return Objects.hash(xh);//根据学号计算哈希值 } //重写toString方法,显示打印数据 @Override public String toString() { return "Student{" + "xh=" + xh + ", name='" + name + '\'' + ", age=" + age + '}'; } }
测试:
@Test public void test1(){ Student std1 = new Student(1, "小明", 11); Student std2 = new Student(1, "小明", 11); System.out.println(std1); System.out.println(std2); System.out.println("std1和std2相同吗?"+std1.equals(std2)); }
结果:
八 抽象类和抽象方法
1 引入
问题:例如前面在定义Animal类时,shout()方法用于表示动物的叫声,但是不同的动物,叫声也是不同的,因此在shout()方法中无法准确描述动物的叫声。如何能使Animal类中既包含shout()方法,又无需提供其方法的实现呢?
解决方法:Java提供了抽象方法来满足这种需求。抽象方法必须使用abstract关键字来修饰,并且在定义方法时不需要实现方法体。当一个类中包含了抽象方法,那么该类也必须使用abstract关键字来修饰,这种使用abstract关键字修饰的类就是抽象类。
2 抽象类及抽象方法定义的语法格式:
// 定义抽象类 [修饰符] abstract class 类名 { // 定义抽象方法 [修饰符] abstract 方法返回值类型 方法名([参数列表]); // 其他方法或属性 }
注意:
包含抽象方法的类必须定义为抽象类,但抽象类中可以不包含任何抽象方法。
抽象类是不可以被实例化的,如果想调用抽象类中定义的抽象方法,需要创建一个子类,在子类中实现抽象类中的抽象方法。
例如:
//抽象类 abstract class Animal{ //抽象方法 public void shout(); } //创建Cat类,继承抽象了Animal class Cat extends Animal{ //实现抽象类中的抽象方法shout() public void shout(){ System.out.println("喵喵..."); } } //创建Dog类,继承抽象了Animal class Dog extends Animal{ //实现抽象类中的抽象方法shout() public void shout(){ System.out.println("汪汪..."); } }
九 接口
1 接口的定义
老版本接口: | 接口是一种特殊的抽象类,它不能包含普通方法,其内部的所有方法都是抽象方法,它将抽象进行的更为彻底。 |
---|---|
JDK8接口定义: | 接口中除了抽象方法外,还可以有默认方法和静态方法(也叫类方法),默认方法使用default修饰,静态方法使用static修改,并且这两种方法都允许有方法体。 |
2 JDK8接口语法格式:
[修饰符] interface 接口名 [extends 父接口1,父接口2,...] { [public] [static] [final] 常量类型 常量名 = 常量值; [public] [abstract] 方法返回值类型 方法名([参数列表]); [public] default 方法返回值类型 方法名([参数列表]){ // 默认方法的方法体 } [public] static 方法返回值类型 方法名([参数列表]){ // 类方法的方法体 } }
注意:
修饰符可以使用public或直接省略(省略时默认采用包权限访问控制符)。 |
---|
在接口内部可以定义多个常量和抽象方法,定义常量时必须进行初始化赋值,定义默认方法和静态方法时,可以有方法体。 |
在接口中定义常量时,可以省略“public static final”修饰符,接口会默认为常量添加“public static final”修饰符。 |
在接口中定义抽象方法时,也可以省略“public abstract”修饰符,定义default默认方法和static静态方法时,可以省略“public”修饰符,这些修饰符系统都会默认进行添加。 |
例如:
public interface UserDao{ int a = 10;//常量,默认修饰符:public static final void addUser();//抽象方法,默认修饰符:public abstract default void test1(){} //默认方法,有方法体{},默认修饰符:public static void test2(){} //静态方法,有方法体{},默认修饰符:public }
总结:
从JDK 8开始,接口中可以包含三类方法,抽象方法、默认方法、静态方法。 |
---|
默认方法和静态方法都可以有方法体,并且静态方法可以直接通过“接口.方法名”来调用。 |
抽象方法和默认方法只能通过接口实现类的实例对象来调用 |
3 接口实现类
接口不能创建对象,接口中抽象方法和默认方法只能通过接口实现类的实例对象来调用。
接口实现类格式:
[修饰符] class 类名 [extends 父类名] [implements 接口1,接口2,...] { ... }
注意:
当一个类实现接口时,如果这个类是抽象类,只需实现接口中的部分抽象方法即可,否则需要实现接口中的所有抽象方法。
一个类可以通过implements关键字同时实现多个接口,被实现的多个接口之间要用英文逗号(,)隔开。
接口之间可以通过extends关键字实现继承,并且一个接口可以同时继承多个接口,接口之间用英文逗号(,)隔开。
一个类在继承一个类的同时还可以实现接口,此时,extends关键字必须位于implements关键字之前。
例如:
当一个类实现接口时,如果这个类是抽象类,只需实现接口中的部分抽象方法即可,否则需要实现接口中的所有抽象方法。
//创建UserDao接口 public interface UserDao{ void addUser(); void updateUser(); void deleteUser(); } //创建UserDao接口实现类:非抽象类实现接口,必须实现接口中的所有抽象方法 public class UserDaoImpl implements UserDao{ //实现接口中的抽象方法:addUser() public void addUser(){} //实现接口中的抽象方法:updateUser() public void addUser(){} //实现接口中的抽象方法:deleteUser() public void addUser(){} } //创建UserDao接口实现类:抽象类实现接口,可以实现接口中的抽象方法,也可以不实现 public abstract UserDaoImpl2 implements UserDao{ //可以实现接口中的抽象方法:adduser() public void addUser(){} //也可以不实现接口中的抽象方法:updateUser()/deleteUser() }
例如:
一个类可以通过implements关键字同时实现多个接口,被实现的多个接口之间要用英文逗号(,)隔开。
interface A{} interface B{} class C implements A,B{} // 一个类可以实现多个接口
例如:
接口之间可以通过extends关键字实现继承,并且一个接口可以同时继承多个接口,接口之间用英文逗号(,)隔开。
interface A{} interface B{} interface C extends A,B{} // 一个接口可以继承多个接口
例如:
一个类在继承一个类的同时还可以实现接口,此时,extends关键字必须位于implements关键字之前
interface A{} class Fu{} class Zi extends Fu implements A{}//一个类可以先继承父类,再实现接口
4 面向接口编程
在系统分析和架构中,分清层次和依赖关系,每个层次不是直接向其上层提供服务(即不是直接实例化在上层中),而是通过定义一组接口,仅向上层暴露其接口功能,上层对于下层仅仅是接口依赖,而不依赖具体类。
这样做的好处是显而易见的,首先对系统灵活性大有好处。当下层需要改变时,只要接口及接口功能不变,则上层不用做任何修改。甚至可以在不改动上层代码时将下层整个替换掉
就像我们将一个WD的60G硬盘换成一个希捷的160G的硬盘,计算机其他地方不用做任何改动,而是把原硬盘拔下来、新硬盘插上就行了,因为计算机其他部分不依赖具体硬盘,而只依赖一个IDE接口,只要硬盘实现了这个接口,就可以替换上去。
使用接口的另一个好处就是不同部件或层次的开发人员可以并行开工,只要接口一致,设计合理,完全可以并行进行开发,从而提高效率。只要接口一致,设计合理,完全可以并行进行开发,从而提高效率。
十 多态
1 多态概述
定义:在Java中,多态是指不同类的对象在调用同一个方法时所呈现出的多种不同行为。
说明:把子类对象直接赋值给父类引用变量时,相同引用类型的变量调用同一个方法所呈现出的多种不同形态。
作用:通过多态,消除了类之间的耦合关系,大大提高了程序的可扩展性和可维护性。
注意:Java的多态性是由类的继承、方法重写以及父类引用指向子类对象体现的。由于一个父类可以有多个子类,多个子类都可以重写父类方法,并且多个不同的子类对象也可以指向同一个父类。这样,程序只有在运行时程序才能知道具体代表的是哪个子类对象,这就体现了多态性。
例如:
class Animal{} class Cat extends Animal{} class Dog extends Amimal{} class Mouse extends Animal{} //多态:父类的引用类型变量指向子类对象 Animal cat = new Cat(); Animal dog = new Dog(); Animal mouse = new Mouse();
多态注意问题:
在多态情况下,子父类存在同名的成员变量时,访问的是父类的成员变量
在多态情况下,子父类存在同名的非静态函数,访问的是子类的函数
在多态情况下,子父类存在同名的静态函数时,访问的是父类的函数
2 类型判断
Java提供了一个关键字instanceof,它可以判断一个对象是否为某个类(或接口)的实例或者子类实例。
格式:
对象(或者对象引用变量) instanceof 类(或接口)
例如:
public void test(Animal animal){ if(animal instanceof Cat){ //animal是cat }else if(animal instanceof Dog){ //animal是Dog }else if(animal instanceof Mouse){ //animal是Mouse } }
十一 内部类
1 什么是内部类
在Java中,允许在一个类的内部定义类,这样的类称作内部类,这个内部类所在的类称作外部类。
2 成员内部类
定义:在一个类中除了可以定义成员变量、成员方法,还可以定义类,这样的类被称作成员内部类。
说明:在成员内部类中,可以访问外部类的所有成员,包括成员变量和成员方法;在外部类中,同样可以访问成员内部类的变量和方法。
例如:
//外部类 class Outer{ String name = "小明"; int age = 11; public void test1(){ System.out.println("Outer中的test1方法"); } //成员内部类 class Inner{ public void test2(){ System.out.println("name="+name); System.out.println("age="+age) test1(); } } }
创建内部类对象的具体语法格式如下:
外部类名.内部类名 变量名 = new 外部类名().new 内部类名();
例如:
Outer.Inner in = new Outer().new Inner(); in.test();
结果:
3 局部内部类
定义:局部内部类,也叫做方法内部类,就是定义在某个局部范围中的类,它和局部变量一样,都是在方法中定义的,其有效范围只限于方法内部。
说明:在局部内部类中,局部内部类可以访问外部类的所有成员变量和方法,而局部内部类中的变量和方法却只能在创建该局部内部类的方法中进行访问。
例如:
//外部类 class Outer{ String name = "小明"; int age = 11; public void test(){ //局部内部类:只在方法内有效 class Inner{ public void print(){ System.out.println("name="+name); System.out.println("age="+age); } } //创建局部内部类对象 Inner in = new Inner(); in.print(); } }
测试:
//创建外部类对象 Outer out = new Outer(); //调用test方法 out.test();
结果:
4 静态内部类
定义:所谓静态内部类,就是使用static关键字修饰的成员内部类。
说明:静态内部类在成员内部类前增加了static关键字,在功能上,静态内部类中只能访问外部类的静态成员,同时通过外部类访问静态内部类成员时,可以跳过外部类从而直接通过内部类访问静态内部类成员。
例如:
//外部类 class Outer{ //外部类静态成员变量 static String country = "中国"; //外部类非静态成员变量 String name = "小明"; //创建静态内部类 static class Inner{ String sex = "男"; int age = 11; public void test(){ System.out.println("country="+country);//静态内部类可以访问外部类的静态成员 //System.out.println("name="+name);//报错:不能访问非静态成员 } } }
创建静态内部类对象的具体语法格式如下:
外部类名.静态内部类名 变量名 = new 外部类名.静态内部类名();
例如:
//创建静态内部类对象 Outer.Inner in = new Outer.Inner(); in.test();
5 匿名内部类(重点)
定义:匿名内部类其实就是没有名称的内部类。
说明:在调用包含有接口类型参数的方法时,通常为了简化代码,可以直接通过匿名内部类的形式传入一个接口类型参数,在匿名内部类中直接完成方法的实现。
注意:从JDK 8开始,允许在局部内部类、匿名内部类中访问非final修饰的局部变量,而在JDK 8之前,局部变量前必须加final修饰符,否则程序编译报错。
创建匿名内部类对象的具体语法格式如下:
new 父接口(){ // 匿名内部类实现部分 }
例如:
//创建一个接口 interface Test{ void test1(); } //匿名内部类:创建Test接口实现类对象 new Test(){ //实现接口中的抽象方法 public void test1(){ } };