目录
面向过程思想: 所谓面向过程的编程思想,简单理解,程序是“动词”的集合,即程序功能是由一系面向过程思想:
所谓面向过程的编程思想,简单理解,程序是“动词”的集合,即程序功能是由一系列有序的动作来完成。
面向对象的思想:
所谓面向对象的编程思想,简单理解,程序是由一系列的对象(消息) + 消息组成,即 程序是由一系列的对象和对象间的消息组成。
访问权限修饰符
在Java语言中,一切事物(类所有成员)都具有(或显示定义或隐式定义的)访问权限,而这种语言层面的访问权限控制,是由访问权限修饰符实现。
访问权限修饰符的访问控制,分为2个层面:
修饰类中成员(field & method)
控制类中的成员,对其他类可见性(其他类是否可以直接使用到)
修饰类
通常用来限定,类库中的类(自定义数据类型),对于外部使用者的可见性(是否能使用该类型)。
1、对类中成员的访问控制
对于类中成员的访问,可以使用的访问权限修饰符有4种:
public:任意类均访问,实际就是没有限制访问权限
a.类体中,可以直接访问
b.同包的其他类中,也可以访问
c.不同包的的类汇总,也可以访问
protected:同包中的其他类,和不同包的(可见)子类均可见
a.类体中,可以直接访问
b.同包其他类中,可以访问
c.在非同包的一部分类中,访问不到,还有一部分(子类中可以访问到,父类中被protected)
(default默认权限,隐式定义):同包中的其他类可见
a.类体中,可以直接访问
b.同包其他类中,可以访问
c.不同包的类中,访问不了
private:仅对同类中的其他成员可见
a.类体中,可以访问
b.同包其他类中,无法访问
c.非同包中,无法访问
package com.wolf;
/**
* @create 2021-07-21 19:47
*/
public class Member {
public double publicValue;
protected boolean protectedValue;
int defaultValue;
private byte privateValue;
public void testAccess() {
System.out.println(this.publicValue);
System.out.println(this.protectedValue);
System.out.println(this.defaultValue);
System.out.println(this.privateValue);
}
}
package com.wolf;
/**
* @create 2021-07-21 20:40
*/
public class TestMember {
public static void main(String[] args) {
//在同包中的其他类中,访问其他类中定义的不同访问权限的成员
Member member = new Member();
System.out.println(member.publicValue);
System.out.println(member.protectedValue);
System.out.println(member.defaultValue);
System.out.println(member.privateValue);// 不能访问
}
}
package com.otherwolf;
import com.wolf.Member;
/**
* @create 2021-07-21 20:47
*/
public class TestMember {
public static void main(String[] args) {
//在不同包中的其他类中,访问其他类中定义的不同访问权限的成员
Member member = new Member();
System.out.println(member.publicValue);
System.out.println(member.protectedValue);// 不能访问
System.out.println(member.defaultValue);// 不能访问
System.out.println(member.privateValue);// 不能访问
}
}
2、对类的访问控制
能够修饰类的访问权限修饰符只有2种:
public:对其他任意类可见(同一个module下),比如:同包中的其他类,以及非同包的其他类
default:对同包中的其他类可见,非同包的类访问不了
为什么要使用访问控制修饰符?private
使用户不要触碰他们“不该”触碰的代码(private)
类库设计者可以安全的修改实现细节(不然public别人也可以用,修改起来不方便)
面向对象三大特征
封装
封装是一种信息隐藏技术,是指将数据和基于数据的操作封装在一起数据被保护在内部
系统的其他部分只有通过在数据外面的被授权的操作才能够进行交互
目的在于将类使用者class user和类设计者class creator分开。
在面向对象的编程中,用类来封装相关的数据和方法,保证了数据的安全和系统的严密性。
针对private成员变量,专门定义public的方法,让外部能够访问私有成员变量的值
get:让外部读取私有成员变量值
set:让外部修改私有成员变量值
同时,为了清楚表明get set 是哪个成员变量的值
命名规则: getXxx(){ } setXxx(修改后目标参数值){ }
注意:
提供get、set方法的好处:
1.可以满足访问私有成员变量值的需求
2.通过定义get、set方法,来实现对私有成员变量的读写分离
3.如果被 private修饰,只能通过set()来修改,别人访问不了
public class Demo3 {
public static void main(String[] args) {
Student student = new Student("zhangsan",18,true,001);
//一旦类中成员,被private,改成员变量的值,就不能被轻易访问
//System.out.println(student.name);// 错误
//比如老师应该有权限访问学生信息
student.setName("lisi");
System.out.println(student.getName());
}
}
class Student {
private String name;
private int age;
private boolean isMale;
private int sno;
public Student(String name, int age, boolean isMale, int sno) {
this.name = name;
this.age = age;
this.isMale = isMale;
this.sno = sno;
}
public String getName() {
return name;
}
public void setName(String name) {
//自己可以根据需要做出检查
if (name == null || name.equals("")) {
return;
}
this.name = name;
}
}
继承(重要)
根据我们刚刚学过的知识,完成如下练习:
分别定义一个表示人和学生的类
表示人的类有姓名属性,以及吃饭,睡觉的行为
表示学生的类有姓名,学号两个属性,同时有吃饭,睡觉,学习3种行为
public class Demo1 {
public static void main(String[] args) {
Student student = new Student();
student.name = "lisi";
System.out.println(student.name);
student.eat();
}
}
class Person {
String name;
public void eat() {
System.out.println(name + "eating");
}
public void sleep() {
System.out.println(name + "sleeping");
}
}
//我们使用继承的方式,复用已有的类定义代码,即继承Person中定义的成员
class Student extends Person {
int sno;
public void study() {
System.out.println(sno + " " + name + "studying");
}
}
在现实生活中我们常常有这样的说法:猫是一种动物
这句话其实描述了两种类型之间的存在的一种从属关系,猫与动物的之间的这种从属关系可以简单表述为“is a”。
那么Java语言中如何描述这种“is a”的关系呢? 继承
被继承的类称之为父类(基类或超类),继承其他类的类称之为子类(派生类,导出类);子类可以通过继承机制,不写任何额外代码就可以拥有父类的“所有”成员。
//父类、基类、超类
class Father{}
//子类、派生类、导出类
class Son extends Father{}
优点:
a.代码复用(类)
b.提高了代码的可维护性(这是一把双刃剑),通过继承,实现代码复用,一定程度深,消除了冗余代码
c.弱化Java中的类型约束(多态的前提)
//弱化Java中的类型约束(多态的前提)
//在没有继承之前是 同类 引用 = new 类();
Student student = new Student();
//现在可以使用父类
Person person = new Student();
缺点:xxx
特点:类只能实现单重继承 只能extends一个类,可间接extends多个父类
注意事项:
1.子类只能访问父类所有非私有的成员(成员方法和成员变量),但可以继承私有成员
2.子类不能继承父类的构造方法
权限修饰符补充:在跨包的子类中,可以访问父类中的protected成员
public class TestMemberAccess extends MemberAccess {
public void testAccess() {
//在子类中直接访问
System.out.println(this.protectedValue);
//创建对象访问
MemberAccess memberAccess = new MemberAccess();
//System.out.println(memberAccess.protectedValue);错误
//通过子类对象可以访问到
TestMemberAccess testMemberAccess = new TestMemberAccess();
System.out.println(testMemberAccess.protectedValue);
}
}
继承相关的几个问题?
1.子类对象的初始化问题在子类对象汇总,父类继承数据和子类自己定义数据,谁先谁后初始化(赋初值)
2.子类和父类中,定义同名成员变量
3.子类和父类中,是否能定义一模一样的方法?
回答:
1、对于一个子类对象而言,其内存中的数据,分成了两部分:
1.从父类中,继承下来成员变量的值
2.子类中,自己定义的成员变量的值
这两部分数据,在给他们赋初值的时候,谁先赋初值谁后赋初值(谁先初始化值,谁后初始化值)?
java语言的结论是:一定是先初始化子类对象中继承的父类变量的值,后初始化子类对象中,子类自己定义的成员变量值【先父后子】
如何保证呢?核心思路:
1.首先,构造方法本身的功能,就是初始化对象成员变量值的
2.所以,显然,对我们只需要保证,父类的构造方法,先于子类的构造方法运行
3.如何让父类构造方法,先于子类构造方法执行(在子类的构造方法的,第一条语句的位置,先去调用父类)
如何具体从语法上,保证先执行父类构造方法,在执行父类构造方法,有两种方式:
1.子类对象的隐式初始化(先调用父类构造方法,jvm隐式完成>
2.子类对象的显示初始化(我们通过代码,完成先调用父类构造方法的功能)
隐式初始化
a.当父类提供了默认的构造函数(无参构造方法)
b.且子类的构造函数中没有显式调用父类的其它构造函数
c.则在执行子类的构造函数之前,(jvm)会自动执行父类的构造函数直到执行完Object的构造函数后才执行子类的构造函数
public class Demo1 {
public static void main(String[] args) {
InitializeSon initializeSon = new InitializeSon(100);
}
}
class InitializeFather {
int fatherI;
public InitializeFather() {
System.out.println("构造方法:父");
}
}
class InitializeSon extends InitializeFather {
int sonI;
public InitializeSon(int sonI) {
//首先调用父类构造方法
System.out.println("构造方法:子");
this.sonI = sonI;
}
}
//构造方法:父
//构造方法:子
显式初始化
子类对象的显式初始化,是我们coder通过代码来保证,先执行父类构造方法,在执行子类构造方法
核心问题是,如何在ava语法层面,显式调用父类的构造方法?通过一个关键字super
super关键字: 父类对象的引用
this关键字:当前对象的引用
super 访问父类对象的成员变量值、成员方法,调用父类中定义的构造方法
this 访问当前对象的成员变量值、成员方法,调用父类中定义的构造方法
注意事项:super 必须在第一条语句位置 与 this 不能共存 只能在类体中使用
public class Demo1 {
public static void main(String[] args) {
ExplicitSon explicitSon = new ExplicitSon(100, 1);
System.out.println(explicitSon.sonE);
System.out.println(explicitSon.FatherE);
}
}
class ExplicitFather {
int FatherE;
public ExplicitFather(int fatherE) {
System.out.println("构造方法:父");
this.FatherE = fatherE;
}
}
class ExplicitSon extends ExplicitFather {
int sonE;
public ExplicitSon(int fatherE, int sonE) {
super(fatherE);
System.out.println("构造方法:子");
this.sonE = sonE;
}
}
2、父类域的隐藏 问题
1.在子类中是否可以定义和父类同名的成员变量? 可以
2.如果可以,那么在子类中访问这个同名变量,究竟访问到的是父类对象中的值,还是子类对象中的值?如果在父类中访问呢? 在子类中访问:子类中这个同名变量的值 在父类中访问:父类中定义的同名变量的值
3.是否可以在子类对象中,同时访问到子类对象和父类对象中的同名成员变量的值? 可以(super.intValue)
上面3个问题的答案综合起来,其实就是说的就是域的隐藏
public class Demo1 {
public static void main(String[] args) {
FieldSon fieldSon = new FieldSon();
fieldSon.sonAccess();
fieldSon.allAccess();
fieldSon.fatherAccess();
}
}
class FieldFather {
int intValue = 100;
public void fatherAccess() {
System.out.println("father" + intValue);//100
}
}
class FieldSon extends FieldFather {
int intValue = 200;
public void sonAccess() {
System.out.println("son" + intValue);//200
}
public void allAccess() {
System.out.println("all-father" + super.intValue);//100
System.out.println("all-son" + intValue);//200
}
}
//son200
//all-father100
//all-son200
//father100
方法覆盖 的问题
1.子类中能否定义和父类一模一样的方法? 可以
2.如果可以,那么在子类类体中访问这个一模一样的方法,究竞访问到的是父类对象中的方法,还是子类对象中的方法?如果在父类中访问呢? 在子类中访问:子类中这个同名的方法 在父类中访问:子类中这个同名的方法
3.是否可以在子类对象中,同时访问到子类和父类中的一模一样的方法?
public class Demo1 {
public static void main(String[] args) {
OverrideSon overrideSon = new OverrideSon();
overrideSon.sonAccess();//OverrideSon
overrideSon.fatherAccess();//OverrideSon
overrideSon.allAccess();//OverrideSon OverrideFather
}
}
class OverrideFather {
public void access() {
System.out.println("OverrideFather");
}
public void fatherAccess() {
//在父类中再也访问不到了
access();
}
}
class OverrideSon extends OverrideFather {
public void access() {
System.out.println("OverrideSon");
}
public void sonAccess() {
access();
}
public void allAccess(){
access();//子类中的方法
super.access();//父类中的方法
}
}
//OverrideSon
//OverrideSon
//OverrideSon
//OverrideFather
当我们调用子类方法,子类方法在运行时,怎么确定,运行哪个方法的?
1.当子类方法执行的时候(如果子类方法调用了其他方法),优先在子类中找目标方法,如果在子类中找到了目标方法,执行子类中定义的目标方法
2.如果说,在子类中,没有找到目标方法,接着到父类中找目标方法,如果找到,就执行父类中定义的方法
方法覆盖,在实际开发中,经常使用的,使用的场景是什么呢?
当子类继承父类之后,想要修改父类的方法实现,就可以使用方法覆盖
public class Demo1 {
public static void main(String[] args) {
Model model = new Model();
model.fly();
}
}
class Base {
public void fly() {
System.out.println("flying");
}
}
class Model extends Base {
//方法覆盖
public void fly() {
System.out.println("no fly");
}
}
//no fly
注意事项:
1.父类中私有方法不能被重写(Override 覆盖或重写)
2.静态方法不能被重写(Override)
public class Demo2 {
public static void main(String[] args) {
NoticeSon noticeSon = new NoticeSon();
noticeSon.testOverride();
}
}
class NoticeFather {
private void privateMethod() {
System.out.println("father private");
}
public static void staticMethod() {
System.out.println("static father");
}
public void testOverride() {
privateMethod();//father private
staticMethod();//static father
}
}
class NoticeSon extends NoticeFather {
private void privateMethod() {
System.out.println("son private");
}
public static void staticMethod() {
System.out.println("static father");
}
}
final关键字
final是 最终 的意思,可以修饰类、变量、成员方法
final修饰类:一旦类被final修饰,该类就不能被继承(最终版本不能被子类覆盖修改)
final修饰方法:一旦方法被final修饰,该方法就不能被继承
final修饰变量:一旦变量被final修饰,该变量只能被赋值一次,赋值之后其值不能被修改
a.对于局部变量而言,如果被final修饰,那么该变量必须在使用之前赋值,且只能赋值一次
b.对于成员变量而言,如果被final修饰,必须在对象创建完毕之前,给对象的该成员变量赋值一次(我们自己赋值)
常量:
a.字面值常量 1 2 3.0 true false "helloworld"
b.自定义常量 被final 修饰的变量就是自定义常量
public class Demo1 {
}
//final修饰类
final class FinalFather {
}
//class FinalSon extends FinalFather{} //Cannot inherit
//final修饰方法
class FinalMethodFather {
public final void finalMethof() {
}
}
class FinalMethodSon extends FinalMethodFather {
//public void finalMethof(){} //cannot override; overridden method is final
}
//final修饰变量
class FinalVariable {
//在对象创建完毕之前的3种赋值方式
final int finalField1 = 1;
final int finalField2;//Variable 'finalField' might not have been initialized
final int finalField3;
{
finalField2 = 2;
}
public FinalVariable() {
finalField3 = 3;
}
public FinalVariable(int a) {
finalField3 = a;//每个构造方法都要给同一个final赋初值
}
public void testLocalFianl() {
final int a;
a = 2;
//a = 3;//Variable 'a' might already have been assigned to
}
}
多态
某一个事物,在不同时刻(或条件下)表现出来的不同状态。
比如,对于一只波斯猫,我们在讨论猫这个主题的时候,我们可以说波斯猫是一只猫:
猫 cat = new 波斯猫();
但是,在我们谈论动物这个主题的时候,我们也可以说波斯猫是一种动物:
动物 animal = new 波斯猫();
还比如,水在不同温度下的状态。
Java语言中的多态指什么呢? “同一个对象”的行为,在不同的时刻或条件下,表现出不同的效果。
要实现多态的效果,有前提条件:
1.继承
2.要有方法覆盖
3.父类引用指向子类对象(对象有时也称之为实例)
多态成员的访问特征:父类引用指向子类对象的时候,如果我们]父类引用,去访问子类对象中的成员
成员变量:
编译看左边,运行看左边
成员方法:
编译看左边,运行看右边
仅从理解的角度来解释:
1.编译看左边:父类引用指向子类对象,此时编译看左边是在说,
通过引用变量可以访问到的子类成员的范围,是由引用类型来决定的
a.迄今为止,我们是怎么去访问一个对象﹖我们都是通过一个中间人即引用变量,间接访问堆上对象
b.也就是说,只有通过引用变量,我才能访问到堆上的对象
举例:
1.我们如果把对象,当成我们的一台电视机,对于电视机而言,我们只能使用,遥控器去操作电视机
2.这也就意味着,遥控器提供了什么样的功能,那我们只能使用,遥控器提供的功能,操作电视机
此时即使电视机提供了很多功能可以使用,但是如果遥控器提供了,极其有限的功能
3.这意味着,我们可以使用的电视机的功能,被遥控器给限制了
所以,回到java程序,访问对象的时候,引用变量,就是我们用来操作对象的"遥控器",所以引用变量的类型决定了,可以访问到的成员的范围。
2.对于成员方法的,运行(结果)看右边(多态)
就是说对于成员方法而言,通过引用变量,实际访问到的行为(方法),是由引用实际指向的对象来决定的
3.对于成员变量,运行看左边
一个对象属性(成员变量)和行为,一个对象的属性(成员变员表示),表示了一对象的外貌
1.在多态中,此时对于子类对象而言,把子类对象赋值给父类类型的引用,就相当于给子类对象披上了一个父类类型马甲,因此,该子类对象看起来,就是一个父类对象(外貌特征,表现出的就应该是父类的外貌特征)
public class Demo1 {
public static void main(String[] args) {
//父类引用指向子类对象
Animal animal = new Cat();
animal.shout();//喵喵喵
testPolymorphism(animal);//喵喵喵
animal = new Dog();
animal.shout();//汪汪汪
testPolymorphism(animal);//swimming 汪汪汪
System.out.println(animal.name);//xxxAnimal
}
public static void testPolymorphism(Animal animal) {
//animal.swimming(); //错误,父类没有,下面修改
if (animal instanceof Dog){
Dog dog = (Dog) animal;
dog.swim();
}
animal.shout();
}
}
class Animal {
String name = "xxxAnimal";
public void eat() {
System.out.println("animal eat");
}
public void shout() {
System.out.println("animal shout");
}
}
class Cat extends Animal {
String name = "xxxCat";
@Override
public void shout() {
System.out.println("喵喵喵");
}
}
class Dog extends Animal {
String name = "xxxDog";
@Override
public void shout() {
System.out.println("汪汪汪");
}
public void swim(){
System.out.println("swimming");
}
}
多态的弊端:不能访问子类特有的功能,只能看左面也就是父类中有的
解决:类型转化来解决 把父类类型的引用 —> 子类类型的引用
引入关键字:instanceof 判断指向对象的实际类型 结果是布尔类型
格式:对象名 (引用变量) instanceof 实际类型名