面向对象三大特征:封装,继承,多态
1.封装 ->封装的核心思想是权限控制,目的是信息保护
当类和类内部的成员被定义好后,基于安全性考虑可能并不希望把所有成员都暴露给外部调用,于是通过权限修饰符控制使得外部无法直接使用部分成员,这个过程就是封装。
public class Oop {
//私有属性,仅当前类可以使用
private int iPR = 0;
//setter方法
public void setiPR(int iPR){
//赋值之前可以对入参进行控制
if(iPR>10) iPR=add(iPR);
this.iPR=iPR;
};
//getter方法
public int getiPR(){
return iPR;
}
//私有方法,仅当前类可以使用
private int add(int i){
return i+1;
}
}
权限修饰符共有public,protected,默认,private四种。将在继承之后统一介绍
权限修饰符也可以修饰外部类,只能使用public和默认
public外部类:该外部类若想被包外的其他类中所访问,可以通过import关键字进行导包。
默认外部类:该外部类无法存在于包外的其他类中,即使导包也没用。
2.继承 ->继承的核心思想是分类(is-a),目的时代码复用
当定义的类越来越多后,为了便于管理要对定义的class进行分类。
同时,为了提高代码复用性,将同一类共同的代码抽出来让所有这一类的class都能使用。
共同使用的代码便重新定义一个class,就是父类。
而其余的class要使用extends关键字和这个公共class产生联系,就是继承。
public class Animal {
//父类-动物
}
class Cat extends Animal{
//子类-猫
}
class Dog extends Animal{
//子类-狗
}
1).Java使用的是单继承,即一个类只能有一个父类,Java中所有类都继承自Object类。
2).final关键字可以修饰类,final修饰的类不可以被继承
3).由于Java的封装性,所以即使是继承关系也不能使用父类所有的成员
四种权限修饰符
private:私有的,本类使用
defalut:默认的,可以省略不写。同包类使用。
protected:受保护的,同包类+不同包子类使用
public:公共的,项目中所有类
package com.springlight;
public class Animal {
//private只能本类内使用
private void m1(){
};
//默认
void m2(){
};;
//受保护的
protected void m3(){
};;
//公共的
public void m4(){
};;
}
class Cat extends Animal{
}
class Dog extends Animal{
}
class Bus{
//同包类
public void test(){
Animal animal = new Animal();
//private修饰的方法不可以使用
//animal.m1();
animal.m2();
animal.m3();
animal.m4();
}
}
package com.springliant2;
import com.springlight.*;
public class Pig extends Animal {
//不同包子类
public void test(){
Animal animal1 = new Animal();
Pig pig = new Pig();
//private修饰的方法不可以使用
animal1.m1();
pig.m1();
//不同包时,默认修饰符修饰的的方法都不可用
animal1.m2();
pig.m2();
//不同包时,protected修饰的方法,声明的父类对象不可用
animal1.m3();
//不同包时,protected修饰的方法,声明的子类对象可用
pig.m3();
//不同包时,public都可用
animal1.m4();
pig.m4();
}
}
class Car {
//不同包非子类
public void test(){
Animal animal1 = new Animal();
Pig pig = new Pig();
//private修饰的方法不可以使用
animal1.m1();
pig.m1();
//不同包时,默认修饰符修饰的的方法都不可用
animal1.m2();
pig.m2();
//不同包时,protected修饰的方法,声明的父类对象不可用
animal1.m3();
//不同包时,protected修饰的方法,声明的子类对象也不可用
pig.m3();
//不同包时,public都可用
animal1.m4();
pig.m4();
}
}
4).继承具有传递性,子类本身也可以是其他类的父类。
5).方法重写:当子类继承父类的方法时,子类不想用父类的实现。可以对该方法重新定义
public class Animal {
void m1(){
System.out.println("父类方法m1");
};
}
class Cat extends Animal{
//通过Override注解表示这是个重写方法
@Override
void m1() {
System.out.println("子类方法m1");
}
}
6).私有方法不能被重写,final修饰的方法不能被重写
public class Animal {
void m1(){
System.out.println("父类方法m1");
};
final void m2(){
System.out.println("父类方法m2");
};
}
class Cat extends Animal{
//这里@Override注解会报错。去掉注解不会报错,但相当于重写定义了新的方法
@Override
void m1() {
System.out.println("子类方法m1");
}
//这里@Override注解会报错
@Override
void m2(){
System.out.println("子类方法m2");
};
}
7.重写方法的访问权限不能下降,权限访问大小排序为:private<默认<protected<public
public class Animal {
void m2(){
System.out.println("父类方法m2");
};
}
class Cat extends Animal{
//会报错
@Override
private void m2(){
}
}
8).super关键字 -> 提示编译器引用父类的成员
public class Animal {
void m1(){
System.out.println("父类方法m1");
};
}
class Cat extends Animal{
//通过Override注解表示这是个重写方法
@Override
void m1() {
System.out.println("父类方法m1");
}
void m2(){
//调用自己的m1方法,也可以用this关键字
m1();
//通过父类对象调用父类的m1方法
super.m1();
}
}
9).有继承的情况下,创建对象时有以下几点
内存一次性分配
只生成一个对象,子类对象包着父类对象,先生成父类对象,再生成子类对象
有多少父类就调用几次构造函数
this指代当前对象,super并不指代父类对象。仅提示编译器指向父类的成员
10).子类对象创建时会首先创建父类对象并调用父类无参构造器,确保子类对象可以使用父类对象的成员
public class Animal {
{
System.out.println("父类构造代码块");
}
public Animal(){
System.out.println("父类无参构造方法");
}
public Animal(int i){
System.out.println("父类有参构造方法");
}
}
class Cat extends Animal{
{
System.out.println("子类构造代码块");
}
public Cat(){
System.out.println("子类无参构造方法");
}
public Cat(int i){
System.out.println("子类有参构造方法");
}
}
public class Main {
public static void main(String[] args) {
Cat cat1 = new Cat();
System.out.println("=======================");
Cat cat2 = new Cat(1);
}
}
上述Main方法执行结果为:
父类构造代码块
父类无参构造方法
子类构造代码块
子类无参构造方法
=======================
父类构造代码块
父类无参构造方法
子类构造代码块
子类有参构造方法
11).当父类没有无参构造方法时,子类会报错。必须在子类构造方法里手动调用父类构造方法,手动指定后将不在调用父类无参的构造方法
public class Animal {
{
System.out.println("父类构造代码块");
}
public Animal(int i){
System.out.println("父类有参构造方法");
}
}
class Cat extends Animal{
{
System.out.println("子类构造代码块");
}
public Cat(){
//会报错,必须使用super关键字手动调用父类构造方法,而且必须放在第一行
super(1);
System.out.println("子类无参构造方法");
}
public Cat(int i){
//会报错,必须使用super关键字手动调用父类构造方法,而且必须放在第一行
super(1);
System.out.println("子类有参构造方法");
}
}
public class Main {
public static void main(String[] args) {
Cat cat1 = new Cat();
System.out.println("=======================");
Cat cat2 = new Cat(1);
}
}
执行结果:
父类构造代码块
父类有参构造方法
子类构造代码块
子类无参构造方法
=======================
父类构造代码块
父类有参构造方法
子类构造代码块
子类有参构造方法
12).创建子类对象的调用过程 ->加载父类,加载子类,创建父类对象,创建子类对象
public class Animal {
static{
System.out.println("父类静态代码块");
}
{
System.out.println("父类构造代码块");
}
public Animal(){
System.out.println("父类无参构造方法");
}
}
class Cat extends Animal{
static{
System.out.println("子类静态代码块");
}
{
System.out.println("子类构造代码块");
}
public Cat(){
System.out.println("子类无参构造方法");
}
public Cat(int i){
System.out.println("子类有参构造方法");
}
}
public class Main {
public static void main(String[] args) {
Cat cat1 = new Cat();
System.out.println("=======================");
Cat cat2 = new Cat(1);
}
}
执行结果:
父类静态代码块
子类静态代码块
父类构造代码块
父类无参构造方法
子类构造代码块
子类无参构造方法
=======================
父类构造代码块
父类无参构造方法
子类构造代码块
子类有参构造方法
13).虽然父类构造方法定义在子类构造方法第一行,但实际执行过程中,父类构造方法要先于于子类构造方法执行,甚至先于子类的构造代码块
public class Animal {
//父类成员变量
int i ;
static{
System.out.println("父类静态代码块");
}
{
System.out.println("父类构造代码块");
//通过构造代码块给父类对象成员变量赋值
this.i=0;
}
public Animal(int i){
System.out.println("父类有参构造方法");
//输出此时的成员变量i
System.out.println(this.i);
//父类构造方法里再次赋值
this.i = 1;
}
}
class Cat extends Animal{
static{
System.out.println("子类静态代码块");
}
{
System.out.println("子类构造代码块");
System.out.println(super.i);
//子类构造代码块给父类对象的成员属性赋值
super.i = 2;
}
public Cat(){
super(1);
System.out.println("子类无参构造方法");
System.out.println(super.i);
}
public Cat(int i){
super(1);
System.out.println("子类有参构造方法");
System.out.println(super.i);
}
}
public class Main {
public static void main(String[] args) {
Cat cat1 = new Cat();
System.out.println("=======================");
Cat cat2 = new Cat(1);
}
}
执行结果
父类静态代码块 --加载父类class,执行父类静态代码块
子类静态代码块 --加载子类class,执行子类静态代码块
父类构造代码块 --创建父类对象,执行父类构造代码块,并给父类对象成员变量i赋值为0
父类有参构造方法 --执行父类有参构造方法
0 --输出父类成员变量i=0,将i赋值为1
子类构造代码块 --执行子类构造代码块
1 --输出此时的父类对象的成员变量i =1,并将i赋值为2
子类无参构造方法 --执行子类构造方法
2 --输出此时的父类对象的成员变量i = 2
=======================
父类构造代码块 --父类子类不必加载,此时创建父类对象,执行父类构造代码块,并给父类对象成员变量i赋值为0
父类有参构造方法 --执行父类有参构造方法
0 --输出父类成员变量i=0,将i赋值为1
子类构造代码块 --执行子类构造代码块
1 --输出此时的父类对象的成员变量i =1,并将i赋值为2
子类有参构造方法 --执行子类构造方法
2 --输出此时的父类对象的成员变量i = 2
3.多态 ->多态的核心思想是方法重写,目的是动态完成同一类对象的不同实现
public class Animal {
public void m1(){
System.out.println("Animal的m1");
};
}
class Cat extends Animal{
@Override
public void m1(){
System.out.println("Cat的m1");
}
}
class Dog extends Animal{
@Override
public void m1(){
System.out.println("Dog的m1");
}
}
public class Main {
public static void main(String[] args) {
Animal animal1 = new Animal();
Animal animal2 = new Cat();
Animal animal3 = new Dog();
animal1.m1();
animal2.m1();
animal3.m1();
}
}
运行结果为:
Animal的m1
Cat的m1
Dog的m1
1).里氏代换原则:任何基类出现的地方,使用派生类都不应该有问题
2).向上转型: 父类声明 = new 子类对象
向下转型(需要强转):子类声明 = (子类)new 父类对象