封装、继承和多态
基础概念
封装:把对象的属性和方法结合城一个独立的整体,隐藏实现细节,并提供对外访问的接口
继承:从已知的一个类中派生出一个新的类,叫子类。子类实现了父类所有非私有化的属性和方法,并根据实际需求扩展出新的行为
多态:多个不同的对象对同一消息作出响应,同一消息根据不同的对象而采用各种不同的方法
代码实现
类的封装
package com.test.entity;
public class Person {
private String name; //现在类的属性只能被自己直接访问
private int age;
private String sex;
public Person(String name, int age, String sex) { //构造方法也要声明为公共,否则对象都构造不了
this.name = name;
this.age = age;
this.sex = sex;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public int getAge() {
return age;
}
}
package com.test;
import com.test.entity.Person;
public class Main {
public static void main(String[] args) {
Person person = new Person("小明",18,"男");
System.out.println(person.getName()); //只能通过调用getName()方法来获取名字
}
}
类的继承
//定义一父类Person
public class Person{
String name;
int age;
String sex;
}
//工人类
public class Worker extends Person{}
//学生类
public class Student extends Person{}
//class前面添加final关键字表示这个类已经是最终形态,不能继承
public final class Person {}
//父类
public class Person {
String name;
int age;
String sex;
public void hello(){
System.out.println("我叫 "+name+",今年 "+age+" 岁了!");
}
}
//子类
public class Student extends Person{
public void study(){
System.out.println("我的名字是 "+name+",我在学习!"); //可以直接访问父类中定义的name属性
}
}
//实际应用
public static void main(String[] args) {
Student student = new Student();
student.study(); //子类不仅有自己的独特技能
student.hello(); //还继承了父类的全部技能
}
//父类存在有参构造方法,子类必须在构造方法中调用
public class Person {
protected String name; //因为子类需要用这些属性,所以说我们就将这些变成protected,外部不允许访问
protected int age;
protected String sex;
protected String profession;
//构造方法也改成protected,只能子类用
protected Person(String name, int age, String sex, String profession) {
this.name = name;
this.age = age;
this.sex = sex;
this.profession = profession;
}
public void hello(){
System.out.println("["+profession+"] 我叫 "+name+",今年 "+age+" 岁了!");
}
}
//子类Student
public class Student extends Person{
public Student(String name, int age, String sex) { //因为学生职业已经确定,所以说学生直接填写就可以了
super(name, age, sex, "学生"); //使用super代表父类,父类的构造方法就是super()
}
public void study(){
System.out.println("我的名字是 "+name+",我在学习!");
}
}
//子类Worker
public class Worker extends Person{
public Worker(String name, int age, String sex) {
super(name, age, sex, "工人"); //父类构造调用必须在最前面
System.out.println("工人构造成功!"); //注意,在调用父类构造方法之前,不允许执行任何代码,只能在之后执行
}
}
//实际使用
public static void main(String[] args) {
Person person = new Student("小明", 18, "男"); //使用父类型的变量,去引用一个子类对象(向上转型)
Student student = (Student) person; //使用强制类型转换(向下转型)
person.Hello(); //父类对象的引用相当于作为父亲来使用,只能访问父类对象的内容
student.study();
}
//[学生]我叫小明,今年 18岁了!
//我叫小明,我在学习
//判断变量引用对象是什么类
public static void main(String[] args) {
Person person = new Student("小明", 18, "男");
if(person instanceof Student) { //我们可以使用instanceof关键字来对类型进行判断
System.out.println("对象是 Student 类型的");
}
if(person instanceof Person) {
System.out.println("对象是 Person 类型的");
}
}
//子类可以定义和父类同名属性
public class Worker extends Person{
protected String name; //子类中同样可以定义name属性
public Worker(String name, int age, String sex) {
super(name, age, sex, "工人");
}
//在子类中直接使用时
public void work(){
System.out.println("我是 "+name+",我在工作!");
//这里的name,依然是作用域最近的哪一个,也就是在当前子类中定义的name属性,而不是父类的name属性
}
//子类存在同名变量,访问父类同名变量
public void work(){
System.out.println("我是 "+super.name+",我在工作!");
//这里使用super.name来表示需要的是父类的name变量
}
}
注:没有
super.super
这种用法,也就是说如果存在多级继承的话,那么最多只能通过这种方法访问到父类的属性
顶层Object类
所有类都默认继承自Object类,除非手动指定继承的类型,但是依然改变不了最顶层的父类是Object类。
所有类都包含Object类中的方法所有类都默认继承自Object类,除非手动指定继承的类型,但是依然改变不了最顶层的父类是Object类。所有类都包含Object类中的方法。
public class Person extends Oject{
//除非我们手动指定要继承的类是什么,实际上默认情况下所有的类都是继承自Object的,只是可以省略
}
public void println(Object x) {
String s = String.valueOf(x); //调用对象的toString方法
synchronized(this){
print(s);
newLine();
}
}
//默认比较两个对象是否为同一个对象,这里得到的肯定是false
public static void main(String[] args) {
Person p1 = new Student("小明", 18, "男");
Person p2 = new Student("小明", 18, "男");
System.out.println(p1.equals(p2));
}
//实现“如果名字、年龄、性别都完全相同,则肯定是同一个人”,需要修改equals的默认实现
方法的重写
方法的重载是为某个方法提供更多种类,而方法的重写是覆盖原有的方法实现
public class Person{
@Override //重写方法一般使用@Override注解
public boolean equals(Object obj) {
if (obj == null) return false;
if (obj instanceof Person) {
Person person = (Person) obj;
return this.name.equals(person.name) && //字符串内容的比较,不能使用==,必须使用equals方法
this.age == person.age && //基本类型的比较跟之前一样,直接==
this.sex.equals(person.sex);
}
return false;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", sex=" + sex + ", profession=" + profession + "]";
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Student("Ming", 18, "male");
Person p2 = new Student("Ming", 18, "male");
System.out.println(p1.equals(p2));
System.out.println(p1);
}
}
true
Person [name=Ming, age=18, sex=male, profession=学生]
基于这种方法可以重写的特性,对于一个类定义的行为,不同的子类可以出现不同的行为;不同的子类,对于同一个方法会产生不同的结果
面向对象编程中多态特性的一种体现
public class Person {
...
public void exam() {
System.out.println("我是考试方法");
}
}
public class Student {
...
@Override
public void exam() {
System.out.println("我是做题蛆,我要拿A");
}
}
public class Worker {
...
@Override
public void exam() {
System.out.println("我是工人,我要拿S");
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Student("Ming", 18, "male");
Person p2 = new Worker("Wang", 20, "male");
p1.exam();
p2.exam();
}
}
如果不希望子类重写某个方法,我们可以在方法前添加
final
关键字,表示这个方法已经是最终形态如果父类中方法的可见性为
private
,那么子类同样无法访问,也就不能重写,但是可以定义同名方法在重写父类方法时,如果希望调用父类原本的方法实现,那么同样可以使用
super
关键字子类在重写父类方法时,不能降低父类方法中的可见性
抽象类由于不是具体的类定义(它是类的抽象)可能会存在某些方法没有实现,因此无法直接通过new关键字来直接创建对象
要使用抽象类,我们只能去创建它的子类对象
具体的实现,需要由子类来完成,而且如果是子类,必须要实现抽象类中所有抽象方法
抽象类由于不是具体的类定义(它是类的抽象)可能会存在某些方法没有实现,因此无法直接通过new关键字来直接创建对象
抽象类一般只用作继承使用,抽象类的子类也可以是一个抽象类
public abstract class Person { //添加abstract关键字,表明此为抽象类
protected String name; //大体和普通类差不多
protected int age;
protected String sex;
protected String profession;
protected Person(String name, int age, String sex, String profession) {
this.name = name;
this.age = age;
this.sex = sex;
this.profession = profession;
}
public abstract void exam(); //抽象类中可以有抽象方法,也就是说这个方法只有定义,没有方法体
}
public class Worker extends Person {
public Worker(String name, int age, String sex) {
super(name, age, sex, "工人");
}
@Override
public void exam() { //子类必须要实现抽象类所有的抽象方法,强制要求,否则无法通过编译
System.out.println("我是工人,做题我不擅长,只能得到D");
}
}
public class Student extends Person { //如果抽象类的子类也是抽象类,那么可以不用实现父类中的抽象方法
public Student(String name, int age, String sex) {
super(name, age, sex, "学生");
}
@Override //抽象类中并非只有抽象方法,1抽象类中也能有正常方法的实现
public void exam() {
System.out.println("我是学生,我要A");
}
}
接口只代表某个确切的功能,接口一般只代表某些功能的抽象,接口包含了一些列方法的定义,类可以实现这个接口,表示类支持接口代表的功能
接口的目标就是将类所具有某些的行为抽象出来
//Study.java
public interface Study { //使用interface表示这是一个接口
void study(); //接口中只能定义访问权限为public抽象方法,其中public和abstract关键字可以省略
}
//Student.java
public class Student extends Person implements Study {
public Student(String name, int age, String sex) {
super(name, age, sex, "学生");
}
@Override
public void study() {
System.out.println("我会学习!");
}
}
//Teacher.java
public class Teacher extends Person implements Study {
protected Teacher(String name, int age, String sex) {
super(name, age, sex, "教师");
}
@Override
public void study() {
System.out.println("我会加倍学习!");
;
}
}
接口不同于继承,接口可以同时实现多个
public class Student extends Person implements Study, A, B, C {} //多个接口的实现使用逗号隔开
接口跟抽象类一样,不能直接创建对象
我们也可以将接口实现类的对象以接口的形式去使用
当做接口使用时,只有接口中定义的方法和Object类的方法,无法使用类本身的方法和父类的方法
接口同样支持向下转型
从Java8开始,接口中可以存在方法的默认实现(如果方法在接口中存在默认实现,那么实现类中不强制要求进行实现)
接口中不允许存在成员变量和成员方法,但是可以存在静态变量和静态方法
public interface Study {
public static final int a = 10; //接口中定义的静态变量只能是public static final的
public static void test(){ //接口中定义的静态方法也只能是public的
System.out.println("我是静态方法");
}
void study();
}
可以直接通过接口名.的方式使用静态内容
接口可以继承自其他接口
接口没有继承数量限制,接口支持多继承
接口的继承相当于是对接口功能的融合
Object类中提供的克隆方法
package java.lang;
public interface Cloneable { //这个接口中什么都没定义
}
实现接口后,需要将克隆方法可见性提升,不然无法使用
public class Student extends Person implements Study, Cloneable { //首先实现Cloneable接口,表示这个类具有克隆的功能
public Student(String name, int age, String sex) {
super(name, age, sex, "学生");
}
@Override
public Object clone() throws CloneNotSupportedException { //提升clone方法的访问权限
return super.clone(); //因为底层是C++实现,我们直接调用父类的实现就可以了
}
@Override
public void study() {
System.out.println("我会学习!");
}
}
public static void main(String[] args) throws CloneNotSupportedException { //这里向上抛出一下异常,还没学异常,所以说照着写就行了
Student student = new Student("小明", 18, "男");
Student clone = (Student) student.clone(); //调用clone方法,得到一个克隆的对象
System.out.println(student);
System.out.println(clone);
System.out.println(student == clone);
}
标签:03,String,Person,age,多态,sex,面向对象,public,name From: https://www.cnblogs.com/microwang/p/18348613原对象和克隆对象,是两个不同的对象,但是他们的各种属性都是完全一样的,此处的
clone
方法是浅拷贝
- 浅拷贝: 对于类中基本数据类型,会直接复制值给拷贝对象;对于引用类型,只会复制对象的地址,而实际上指向的还是原来的那个对象,拷贝个基莫。
- 深拷贝: 无论是基本类型还是引用类型,深拷贝会将引用类型的所有内容,全部拷贝为一个新的对象,包括对象内部的所有成员变量,也会进行拷贝。