目录
this关键字(原理、理解)
基本概念:在构造方法中和成员方法中访问成员变量时,编译器会加上this.的前缀,而this.相当于汉语中的“我的",当不同的对象调用同一个方法时,由于调用方法的对象不同导致this关键字不同,从而this.方式访问的结果也就随之不同。
使用方式
(1)当形参变量名与成员变量名相同时,在方法体中会优先使用形参变量(就近原则),若希望使用 成员变量,则需要在成员变量的前面加上this.的前缀,明确要求该变量是成员变量。
(2)在构造方法的第一行可以使用this()的方式来调用本类中的其他构造方法(了解)
封装
基本概念:通常情况下可以在测试类给成员变量赋值一些合法但不合理的数值,无论是编译阶段还是运行阶段都不会报错或者给出提示,此时与现实生活不符。为了避免上述错误的发生,就需要对成员变量进行密封包装处理,来隐藏成员变量的细节以及保证成员变量数值的合理性,该机制就叫做封装
实现流程
(1)私有化成员变量,使用private关键字修饰
(2)提供公有的get和set方法,并在方法体中进行合理值的判断
(3)在构造方法中调用set方法进行合理值的判断
实体类的封装:
在项目开发中,通常com.XXXX.bean;com.XXXX.domain;com.XXXX.entity;com.XXXX.pojo
这些包当中通常情况下存放的都是实体类
实体类封装的步骤:
1、私有化成员变量
2、提供公有的get/set方法
3、提供无参/有参/全参的构造方法
4、重写toString()、equals和hashCode()
下面给出练习:
编程实现教育类(Edu)的封装
特征有:
int id;
int userId;
String start;
String end;
String school;
String study;
String description;
Edu next;
提供公有的set和get方法,全参和无参构造方法,重写toString方法和equals方法
有参(int userId, String start, String end, String school, String study, String description)
有参(int id, int userId, String start, String end, String school, String study, String description)
提示:idea中“generate...”即“生成”的快捷键:Alt + Insert(笔记本不行的话试试Alt + Fn + Insert)
import java.util.Objects;
public class Edu {
private int id;
private int userId;
private String start;
private String end;
private String school;
private String study;
private String description;
private Edu next;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getStart() {
return start;
}
public void setStart(String start) {
this.start = start;
}
public String getEnd() {
return end;
}
public void setEnd(String end) {
this.end = end;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
public String getStudy() {
return study;
}
public void setStudy(String study) {
this.study = study;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Edu getNext() {
return next;
}
public void setNext(Edu next) {
this.next = next;
}
public Edu() {
}
public Edu(int id, int userId, String start, String end, String school, String study, String description, Edu next) {
this.id = id;
this.userId = userId;
this.start = start;
this.end = end;
this.school = school;
this.study = study;
this.description = description;
this.next = next;
}
@Override
public String toString() {
return "Edu{" +
"id=" + id +
", userId=" + userId +
", start='" + start + '\'' +
", end='" + end + '\'' +
", school='" + school + '\'' +
", study='" + study + '\'' +
", description='" + description + '\'' +
", next=" + next +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Edu edu = (Edu) o;
return id == edu.id &&
userId == edu.userId &&
Objects.equals(start, edu.start) &&
Objects.equals(end, edu.end) &&
Objects.equals(school, edu.school) &&
Objects.equals(study, edu.study) &&
Objects.equals(description, edu.description) &&
Objects.equals(next, edu.next);
}
public Edu(int userId, String start, String end, String school, String study, String description) {
this.userId = userId;
this.start = start;
this.end = end;
this.school = school;
this.study = study;
this.description = description;
}
public Edu(int id, int userId, String start, String end, String school, String study, String description) {
this.id = id;
this.userId = userId;
this.start = start;
this.end = end;
this.school = school;
this.study = study;
this.description = description;
}
}
继承
发生在父子之间 父类(基类)
公共类中已有特征和行为而在多个类型只需要编写自己独有特征和行为的机制,叫做继承。 使用继承提高了代码的复用性,可维护性以及扩展性。在ava语言中使用extends(扩展)关键字来表示继承关系。
java 只支持单继承,可以有多重继承,一个子类只能有一个父类,而一个父类可以有多个子类
注意:子类 is a 父类
如: public class Worker extends Person{} 表示Worker类继承自Person类,其中Person类叫做超类、父类、基类,Worker类叫做派生类、子类、孩子类。
注意事项
(1)子类不能继承父类的构造方法和私有方法,但私有成员变量可以被继承只是不能直接访问。
(2)无论使用何种方式构造子类的对象时都会自动调用父类的无参构造方法,来初始化从父类中继承的成员变量,相当于在构造方法的第一行增加代码:super()的效果。
super:
通过super,可以访问父类构造方法(通过super的构造方法的代码,必须写在子类构造方法的第一行)
通过super,可以访问父类的属性
通过super,可以访问父类的方法
(3)使用继承必须满足逻辑关系:子类 is a 父类,也就是不能滥用继承。
(4)java语言中只支持单继承不支持多继承,也就是说一个子类只能有一个父类,但一个父类可以有多个子类。
方法的重写(Override)
方法的重载(方法名相同,参数类型,个数,顺序不同)
(1)基本概念:从父类中继承下来的方法不满足子类的需求时,就需要在子类中重新写一个和父类一样的方法来覆盖从父类中继承下来的版本,该方式就叫做方法的重写。
(2)重写的原则
a.要求方法名相同、参数列表相同以及返回值类型相同,从jdk1.5开始允许返回子类类型。
b.要求方法的访问权限不能变小,可以相同或者变大。
c.要求方法不能抛出更大的异常(异常机制)。
d.声明为static和private的方法不能被重写,但是能够被再次声明。
面试题:重写(Override)和重载(Overload)的区别
1、发生的位置
重载:一个类中
重写:子父类中
2、参数列表限制
重载:必须不同
重写:必须相同
3、返回值类型
重载:与返回值类型无关
重写:返回值类型必须一致
4、访问权限:
重载:与访问权限无关
重写:重写后(子)的方法的访问权限不能小于被重写(父)方法的权限
5、异常处理:
重载:跟异常没有关系
重写:异常范围可以小,但是不能抛出新的异常
练习:编程实现Animal类的封装,特征有:名字和毛色,要求提供打印所有特征的方法。编程实现Dog类的封装并继承自Animal类,该类的特征有:牙齿数量,要求提供打印所有特征的方法编程实现DogTest类,在main方法中分别使用无参和有参方式构造Dog类型对象并打印特征。
package animal;
public class Animal {
private String name;
private String color;
public Animal() {
}
public Animal(String name, String color) {
this.name = name;
this.color = color;
}
public String getName() {
return name;
}
public String getColor() {
return color;
}
public void show(){
System.out.println("我的名字叫做" + getName() + ",我的颜色是:" + getColor() + "色");
}
}
package animal;
public class Dog extends Animal{
private int teethNumber;
public Dog() {
}
public Dog(String name, String color, int teethNumber) {
super(name, color);
this.teethNumber = teethNumber;
}
public int getTeethNumber() {
return teethNumber;
}
@Override
public void show(){
System.out.println("我叫" + getName() + ",我的颜色是" + getColor() + ",牙齿数量是:" + getTeethNumber());
}
}
package animal;
public class DogTest {
public static void main(String[] args) {
Dog dog = new Dog();
dog.show();
Dog dog1 = new Dog("亚里", "粉色", 1);
dog1.show();
}
}
运行结果:
访问权限
常用访问权限:
a.public修饰的成员可以在任意位置使用;
b.private修饰的成员只能在本类内部使用;
c,通常情况下,成员方法都使用public关键字修饰,成员变量都使用private关键字修饰:
包的声明
package 包名;→表示声明单层包
package 包名1.包名2.包名3...包名n;→表示多层包,为了便于管理以及解决命名冲突的问题
练习: 自定义矩形(Rect)类,特征有:横纵坐标、长度及宽度,提供无参,全参构造,set/get方法要求实现封装并提供打印方法;
自定义园形(Circle)类,特征有:横纵坐标、半径,提供无参,全参构造,set/get方法,要求实现封装并提供打印方法;提取共性自定义图形(5hape)类,提供无参,全参构造,set/get方法。自定义ShapeTest类,自定义成员方法实现既能打印矩形又能打印圆形的方法并调用
package shape;
/**
* 三种实体类用前文知识即可写出,这里不再赘述,只给出ShapeTest类。
*/
public class ShapeTest {
/*
public static void printRect(Rect rect){
rect.show();
}
public static void printCircle(Circle circle){
circle.show();
}
*/
/** 既能打印矩形,又能打印圆形 */
public static void printShape(Shape shape){
shape.show();
}
/** static 修饰的方法只能调用静态的方法 */
public static void main(String[] args) {
/*
Shape shape = new Shape(3,4);
shape.show();
Rect rect = new Rect(1,2,3,4);
rect.show();
Circle circle = new Circle(1,2,4);
circle.show();
*/
// printRect(new Rect(1,2,3,4));
// printCircle(new Circle(1,2,4));
/** 父类型 引用变量名 = new 子类类型(); */
/**
Shape shape = new Rect(1,2,3,4);
shape.show();
Shape shape1 = new Circle(1,2,4);
shape1.show();
*/
printShape(new Shape(3,4));
printShape(new Rect(1,2,3,4));
printShape(new Circle(1,2,4));
}
}
运行结果:
final关键字
final关键字用于修饰属性、变量(表示变量变为了常量,无法再对其再次进行赋值)
final修饰的局部变量,只能赋值一次(可以先声明后赋值)
final修饰的成员变量时,必须在声明时赋值
final关键字用于修饰类
final修饰类,其不可以被继承
final关键字用于修饰方法
final修饰方法,不允许被子类重写
全局常量(public static final)
全局常量的命名规范:
由一个或多个单词组成,单词与单词之间必须使用下划线隔开,单词中所有字母大写。
抽象类
概念
抽象类必须使用abstract class声明
一个抽象类中可以没有抽象方法。抽象方法必须写在抽象类或者接口中。
格式:
abstract class 类名{ //抽象类
public abstract void 方法名(); // 抽象方法,只有声明而无实现
抽象方法
只声明而未实现的方法称为抽象方法(未实现指的是:没有"{}"方法体),抽象方法必须使用abstract关键字声明。
格式:
abstract class 类名{ //抽象类
public abstract void 方法名(); //抽象方法,只声明而未实现
不能被实例化
在抽象类的使用中有几个原则:
抽象类本身是不能直接进行实例化操作的,即:不能直接使用关键字new完成。
一个抽象类必须被子类所继承。被继承的子类(如果不是抽象类)则必须覆写(重写)抽象类中的全部抽象方法。
常见问题
1、抽象类能否使用final声明?
不能,因为final修饰的类是不能有子类的,而抽象类必须有子类才有意义,所以不能。
2、抽象类能否有构造方法?
能有构造方法,而且子类对象实例化的时候的流程与普通类的继承是一样的。都是要先调用父类中的构造方法(默认是无参的),之后再调用子类自己的构造方法。
抽象类和普通类的区别
1、抽象类必须用public或protected修饰(如果为private修饰,那么子类则无法继承,也就无法实现其抽象方法)。默认缺省为public
2、抽象类不可以使用new关键字创建对象,但是在子类创建对象时,抽象父类也会被JVM实例化。
3、如果一个子类继承抽象类,那么必须实现其所有的抽象方法。如果有未实现的抽象方法,那么子类也必须定义为abstract类
接口
概念
如果一个类中的全部方法都是抽象方法。全部属性都是全局常量。那么此时就可以将这个类定义成一个接口。
定义格式:
interface 接口名称{
全局常量;
抽象方法;
}
面向接口编程思想
这种思想是接口是定义(规范,约束)与实现(名实分离的原则)的分离。
优点:
1、降低程序的耦合性
2、易于程序的扩展
3、有利于程序的维护
全局常量和抽象方法的简写
因为接口本身都是由全局常显和抽象方法组成。所以接口中的成员定义可以简写:
1、全局常星编写时。可以首略public static final关键字,例如:
public static final String INEO =“内容”;
简写后:
String INEO =内容”;
2、抽象方法综写时。可以省略public abstract关键字,例:
public abstract void print() ;
简写后:
void print();
接口的实现implements
接口可以多实现:
格式:
class 子类 implements 父接口1,父接口2...{
}
以上的代码称为接口的实现。那么如果一个类即要实现接口,又要继承抽象类的话,则按照以下的格式编写即可:
class 子类 extends 父类 impleents 父接口1,父接口2. . .{
}
接口的继承
接口因为都是抽象部分,不存在具体的实现,所以允许多继承,例如:
interface C extends A,B{
}
注意
如果一个接口想要使用,必须依赖子类。子类(如果不是抽象类的话)要实现接口中的所有抽象方法。
面试题:接口和抽象类的区别
1、抽象类要被子类继承。接口要被类实现。
2、接口只能声明抽象方法,抽象类中可以声明抽象方法,也可以写非抽象方法。
3、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
4、抽象类使用继承来使用,无法多继承。接口使用实现来使用,可以多实现。
5、抽象类中可以包含static方法,但是接口中不允许(静态方法不能被子类重写,因此接口中不能声明静态方法)。
6、接口不能有构造方法。但是抽象类可以有。
多态
基本概念:多态主要指同一种事物表现出来的多种形态。
饮料:可乐、雪碧、乐虎、加多宝、红牛、脉动...
宠物:猫、狗、鸟、鱼、...
人:学生、教师、工人、保安、...
语法格式:父类类型 引用变量名 = new 子类类型();
//父类型的引用指向子类型的对象【多态的核心】
如: Person pw = new Worker();
Shape shape = new Rect;
pw.show();
示例:
public interface Runner {
/**
* 描述奔跑行为
*/
void running();
}
public interface Hunter extends Runner{
/**
* 描述捕猎行为
*/
void hunt();
}
public class Chinese implements Hunter{
@Override
public void hunt() {
System.out.println("抓到一只小白兔");
}
@Override
public void running() {
System.out.println("正在全力的奔跑");
}
}
public class ChineseTest {
public static void main(String[] args) {
//接口类型的引用指向实现类的对象,形成多态
Runner runner = new Chinese();
runner.running();
Hunter hunter = new Chinese();
hunter.hunt();
}
}
运行结果:
练习:
编程实现Person类的封装,特征有:姓名和年龄,要求提供打印所有特征的方法。编程实现Worker类的封装并继承自Person类,特征有:薪水
编程实现PersonWorkerTest类,在main方法中分别创建Person和Worker类型对象并打印特征。
解析:由程序结果可知,编译阶段调用Person类中的show方法,运行阶段调用Worker类中的show方法
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void showAll(){
System.out.println("姓名:" + name + " 年龄:" + age);
};
}
public class Worker extends Person{
private double salary;
public Worker() {
}
public Worker(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
@Override
public void showAll() {
System.out.println("姓名:" + getName() + " 年龄:" + getAge() + " 薪水为:" + salary);
}
}
public class PersonWorkerTest {
public static void main(String[] args) {
Person p = new Person("张三", 17);
p.showAll();
Worker w = new Worker("李四", 25, 6000);
w.showAll();
Person p1 = new Worker("王五", 32, 15000);
p1.showAll();
}
}
运行结果:
多态的效果
(1)当父类类型的引用指向子类类型的对象时,父类类型的引用可以直接调用父类独有的方法
(2)当父类类型的引用指向子类类型的对象时,父类类型的引用不可以直接调用子类独有的方法
(3)对于父子类都有的非静态方法来说,编译阶段调用父类版本,运行阶段调用子类重写的版本
(4)对于父子类都有的静态方法来说,编译和运行阶段都调用父类版本
引用数据类型之间的转换
(1)引用数据类型之间的转换方式有两种:自动类型转换和强制类型转换。
自动类型转换主要指小类型向大类型的转换,也就是子类转换为父类。
强制类型转换主要指大类型向小类型的转换,也就是父类转换为子类。
(2)引用数据类型之间的转换必须发生在父子类之间,否则编译报错。
(3)若强转的目标类型并不是该引用真正指向的数据类型时则编译通过,运行阶段发生类型转换异常。
(4)为了避免上述错误的发生,应该在强转之前进行判断,格式如下:if(引用变量 instanceof 数据类型)-判断引用变量指向的对象是否为后面的数据类型
实际意义:多态的实际意义在于屏蔽不同子类的差异性实现通用的编程带来不同的效果。