面向对象共有三个特征:封装,继承,多态。
封装
封装表现:
(1)方法就是一个最基本封装体。
(2)类其实也是一个封装体。
封装的好处:
(1)提高了代码的复用性。
(2)隐藏了实现细节,还要对外提供可以访问的方式。便于调用者的使用。
(3)提高了安全性。
关键字 私有 private
public class Person {
String name;
void eat() {
System.out.println(name + "在吃饭");
}
}
public class Test {
public static void main(String[] args) {
//测试:Car类中的run方法。
//1、创建 Person 类型的对象,并把该对象赋值给变量 person,或者说让变量 person 指向Person 类型的对象
Person person1 = new Person();
Person person2 = new Person();
//2、通过已有的对象调用该对象的属性,对该对象的属性进行赋值或者赋值 对象.属性
person1.name = "小明";
person2.name = "小强";
//3、通过已有的对象调用该对象的方法 对象.方法名(参数列表)
person1.eat();
person2.eat();
}
}
通过上述代码发现,虽然我们用Java的属性的行为可以任意访问和使用。
可是怎么才能不让访问呢?需要使用一个Java中的关键字也是一个修饰符 private(私有,权限修饰符)。只要将Person的属性和行为私有起来,这样就无法直接访问。
public class Person {
private String name;
void eat() {
System.out.println(name + "在吃饭");
}
}
这样虽然将人的名字私有了,但是这样就无法给 name 属性赋值了,所以还需要提供访问方式,只要对外提供可以访问的方法,让其他程序访问这些方法。
一般对成员属性的访问动作:赋值(设置 set),取值(获取 get),因此对私有的变量访问的方式可以提供对应的 setXxx 或者 getXxx 的方法,如果方法私有的话,外部无法调用,只能通过 Person
类的公共方法(如 doing()
)间接调用。。
public class Person {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
private void eat() {
System.out.println(name + "在吃饭");
}
public void doing(){
eat();
}
}
public class Test {
public static void main(String[] args) {
Person person1 = new Person();
Person person2 = new Person();
person1.setName("小明");
person2.setName("小强");
person1.doing();
person2.doing();
}
}
打印结果:
这样同样实现了对属性的定义和方法的使用。
总结:
类中不需要对外提供的内容都私有化,包括属性和方法。
以后再描述事物,属性都私有化,并提供 setXxx 和 getXxx 方法对其进行访问。
注意:
私有仅仅是封装的体现形式而已。
private 它表示私有的,是一个权限修饰符。 用关键字private修饰成员变量的时候,可以将属性私有化,在其他类里面不能够直接访问属性,但 是在本类当中可以访问。
以后类的成员变量都是用private修饰,只有想让成员变量能够被访问的时候它才能够被访问。
那是不是想让某个属性可以被访问就去掉该属性前面的关键字private?
不是,我们可以提供专门的访问方式,对外提供可以方法方法,让其他类或者程序通过访问此方法去访问到属性。同时我们还可以在方法中对一些属性的数据进行验证。
如:
public class Person {
private String name;
public void setName(String name) {
// 由于是设置成员变量的值,这里可以加入数据的验证
if (name.length() < 2) {
System.out.println("名字的长度不能小于2");
this.name = "默认名";
return;
}
this.name = name;
}
public String getName() {
return name;
}
private void eat() {
System.out.println(name + "在吃饭");
}
public void doing(){
eat();
}
}
public class Test {
public static void main(String[] args) {
Person person1 = new Person();
Person person2 = new Person();
person1.setName("哼");
person2.setName("小强");
person1.doing();
person2.doing();
}
}
打印结果:
这里就对人的名字属性做了校验,如果名字的长度小于2就给一个默认的名字并返回。
this关键字
当在方法中出现了局部变量和成员变量同名的时候,那么在方法中怎么区别局部变量成员变量呢? 可以在成员变量名前面加上this.来区别成员变量和局部变量。
示例:
public class Person {
private String name; //成员变量
public void setName(String name) {
this.name = name;
}
private void eat() {
String name = "小明"; // 局部变量
System.out.println(name + "在吃饭"); // 调用的局部变量
System.out.println(this.name + "在吃饭"); // 调用的成员变量
}
public void doing(){
eat();
}
}
public class Test {
public static void main(String[] args) {
Person person= new Person();
person.setName("小欣");
person.doing();
}
}
打印结果:
this代表的是对象,方法当中的 this,谁去调用这个方法,this 就代表谁。当对象刚刚创建出来 的时候,this 就已经存在了。
成员变量和局部变量的区别:
1、定义的位置不同
成员变量:定义在类中,但是定义在方法之外的变量,我们称之为成员变量;
局部变量:定义在在类的方法中或者{}语句里面的的变量;
2、在内存中的位置不同
成员变量:存储在堆内存的对象中的;
局部变量:存储在栈内存的方法中;
3、初始化不同
成员变量:因为存储在堆内存中,所以有一个默认的初始化值;
局部变量:没有默认的初始化值,必须手动的进行赋值,然后才可以使用;
4、声明周期不同
成员变量:随着对象的出现而出现在堆内存中,随着对象的消失而消失;
局部变量:随着方法的运行而出现在栈内存中,随着方法执行结束而消失;
继承
继承的概念
Java中类的继承,是指在现有类的基础上去构建声明一个全新的类,构建出来的新类我们称之为子类, 现有的类我们称之为父类,子类会自动拥有父类所有可继承的属性和方法,同时子类还可以拥有自己独有的属性和方法。
继承的格式 在写程序的时候,如果想声明一个类继承另一个类,需要使用extends关键字
class 子类 extends 父类{
}
继承的好处
(1)提高了代码的复用性,提高开发效率。
(2)继承让类与类之间产生了关系,为多态提供了前提。
继承的注意事项:
(1)Java 中类只支持单继承,不允许多继承,也就说一个类只能够有一个直接父类。
(2)多个类可以同时继承自同一个父类,一个父类允许有多个子类。
(3)继承可以是多层继承,也就是说一个类是其他类的子类的同时,又可以是另一个类的父类。
(4)在 Java 当中,子类和父类是一种相对的概念。
(5)所有的类都是间接或者直接继承自Object类,Object类称之为根类或者祖宗类。
子父类中成员的特点
子父类中成员变量的特点
(1)子父类中出现不同名的成员变量,这个时候访问没有任何问题。
(2)子父类中出现同名的成员变量的时候,在子类中直接访问成员变量,实际上访问的是子类的成员变 量而非父类的成员变量,如果想要在子类中访问父类的成员变量,必须使用关键字 superr,但是如果这个成员变量是私有的(private修饰的),即使使用了super关键字也不能够访 问。
示例:
public class Fu {
String name = "爸爸"; //成员变量
}
public class Zi extends Fu{
String name = "儿子"; //成员变量
void speak(){
System.out.println(super.name + "对" + name + "说");
}
}
public class Test {
public static void main(String[] args) {
Zi zi = new Zi();
zi.speak();
}
}
打印结果:
子父类中成员方法的特点
当程序在子类中调用方法的时候,首先会在子类中查找有没有对应的方法,如果子类中存在这样的方法就执行子类中相应的方法,如果子类中不存在就会到父类中去查找,依次类推,如果父类中也不存在,则去父类的父类中查找。
子父类中出现了相同的方法的时候,如果子类想要访问调用父类的方法的时候,就需要使用关键字 super。
示例:
public class Fu {
void speak(){
System.out.println("爸爸在说话");
}
}
public class Zi extends Fu{
void speak(){
super.speak(); //调用父类中的 speak 方法
System.out.println("儿子在说话");
}
}
public class Test {
public static void main(String[] args) {
Zi zi = new Zi();
zi.speak();
}
}
打印结果:
方法的重写(override)
子父类中出现了一模一样的方法时(方法声明部分一模一样,但是方法体不一定一样),会出现覆盖操作,即子类的方法会把父类的方法覆盖掉,我们称之为重写、override、覆盖、复写。
示例:
public class Fu {
void speak(){
System.out.println("爸爸在说话");
}
}
public class Zi extends Fu{
void speak(){
System.out.println("儿子在说话");
}
}
public class Test {
public static void main(String[] args) {
Zi zi = new Zi();
zi.speak();
}
}
打印结果:
方法重写的作用:
当子类需要父类的功能,但是对于功能主体(方法体)子类又拥有自己独特的内容,如果子类直接继承父类的方法肯定不合适,这个时候我们可以把从父类那继承过来的方法的方法体部分进行重写,这 样子类既继承了父类的功能,但是又对该功能进行了独有的实现。
方法重写的注意点:
(1)子类重写父类的方法的时候,必须保证子类方法的权限大于等于父类方法的权限的。
(2)所谓的一模一样并不是单单指方法名一样,而是要求方法的返回值类型、方法名、参数列表都一模 一样。
方法重写和重载的区别?
(1)重载是指同一个类中的多个方法,而重写指的是拥有父子关系的不同类中的方法。
(2)重载指的是方法名相同,参数列表不同的多个方法,跟返回值类型无关。
(3)重写指的是子父类中除了方法体之外完全相同的两个方法(返回值类型、方法名、参数列表都要求 一样)。
抽象类
有时候,某个父类只知道子类应该包含有哪些方法,但是无法确定子类是如何具体实现这些方法的。这个时候我们也可以向上抽取,但是只能够抽取方 法的声明,不能够抽取方法主体,这个时候会出现一种只有声明但是没有具体如何实现(方法体)的方法,这种方法就叫 抽象方法。
抽象方法必须使用关键字 abstract修饰,同时定义了抽象方法的类变成了抽象类,该类也需要使用关 键字 abstract修饰;
抽象类&抽象方法的定义
//抽象类
修饰符 abstract class 类名{
//抽象方法
public abstract 返回值类型 方法名(参数列表);
}
抽象类的特点
(1)由于抽象方法所在的类必定是抽象类,抽象方法一定要定义在抽象类当中。
(2)抽象类中可以没有抽象方法。
(3)抽象类中既可以有抽象方法,也可以拥有非抽象方法。
(4)抽象类不能够直接创建对象。 原因:如果抽象类可以创建对象,那么就可以调用抽象方法,抽象方法没有方法体,调用没有任何意 义;
(5)一个类如果继承自抽象类,要么这个类本身就是一个抽象类,要么对抽象类里面所有的抽象方法进行重写。
抽象类一定是一个父类吗?为什么?
抽象类一定是一个父类,因为它是通过不断抽取而来的;但是从语法角度讲,抽象类可以不是父类, 但是这样没有任何意义。
抽象类可以没有抽象方法,那么既然可以没有抽象方法,那么还把该类定义成抽象类还有意义么? 有意义的,我们可以让该类不能够直接创建对象,该类中定义的方法可以让该类的子类去使用;
抽象的关键字 abstract 不能够和哪些关键字共存?
(1)private:对于父类的私有方法,子类是无法继承得到并进行覆盖重写吗,如果abstract和private 一起使用修饰方法,如果abstract修饰的抽象方法需要子类去重写实现,但是private修饰的方法子类根本无法继承得到并重写,它们之间相同冲突矛盾;
(2)final:它表示最终的,也就是说被fianl修饰的方法不能够进行改变重写,跟abstract修饰的抽象方法需要子类去重 写实现相矛盾;
(3)static:被static修饰的方法属于类,如果在父类中有一个static修饰的方法,子类可以继承使用, 但是不能够进行重 写(因为该方法属于父类的),如果在子类中出现了一个跟父类一模一样的被static修饰的方法,那么子类中的方法 是属于子类,父类的方法属于父类,子类中的方法并不是对父类方法的重写;
总结:静态方法能够被继承,但是不能被重写,跟abstract修饰的抽象方法需要子类去重写实现相矛盾。
final关键字的特点:
(1)final修饰类 被final修饰的类不可以被继承,即不能够派生子类,但是它可以继承自其他类(可以有父类);
(2)修饰方法 被final修饰的方法不能够被重写(覆盖、@Override),但是可以被继承;如果连继承都不想被继 承,我们可以使用关键字private修饰;
(3)修饰成员变量当创建完对象之后,我们不可以对final修饰的成员变量重写赋值;
(4)修饰局部变量 被final修饰的基本类型的变量(包括String类型),被称之为常量,因为这些变量只能够被赋值一 次,永远代表的是基本类型的数据或者String类型的某个字符串;被final修饰的引用类型的变量,它指向的是某个对象的地址,也就是说这个变量指向的地址不能够改变,即不能够再指向其他的对象,只能表示该地址对应的对象,但是虽然地址不能够 改变,但是地址对应的对象的本身的属性是可以改变的。
小案例:
(1)根据人类信息的描述,确定每个人都有名字、要进行工作。把这些共同的属性与工作抽取到父类中(人类),关于工作的内容由具体的人来进行指定,工作内容: 学生:xx正在写作业;老师:xx正在改作业;
(2)创建老师和学生对象,完成工作方法的调用;
示例:
定义人类(抽象类)
public abstract class Person {
public String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
abstract void work();
}
定义老师类 Teacher 继承 人类 Person:
public class Teacher extends Person {
@Override
void work() {
System.out.println(getName() + "正在改作业");
}
}
定义学生类 Student 继承 人类 Person:
public class Student extends Person{
@Override
void work() {
System.out.println(getName() + "正在写作业");
}
}
在测试类中,创建老师和学生对象,完成工作方法的调用:
public class Test {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.setName("张老师");
Student student = new Student();
student.setName("小明");
teacher.work();
student.work();
}
}
打印结果: