🍓作者@ Autumn60
🏄欢迎关注: 👍点赞 🙌收藏 ✍️留言
👲今日鸡汤:每一个决定转身的人,都曾在风里站了很久,只要你想开始,一切都不算太晚,大胆努力的往前走吧,这样耀眼的样子真的很好!
![](https://img-blog.csdnimg.cn/img_convert/88fdc234ef97e205ab0f2f8285e31b01.png)
面向对象的三大特性: 封装,继承,多态
一、封装
将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互,就是把类的实现细节进行了隐藏
例如:计算机开机,你只需按开机键开机,至于它是怎么开的你就不需要管了,对外只提供一个开关机的按钮,内部进行了隐藏
在Java当中:主要通过类和访问权限来实现封装,访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:
1.访问修饰限定符(访问权限)
![](https://img-blog.csdnimg.cn/img_convert/dd832f9e21efe4d42794365c4189cccf.png)
先看完包再返回来看效果更好
private:被private所修饰的属性,只能在当前的类当中所使用,在类的大括号里面使用
default:在你什么都不写的时候,他就是默认的权限,也称包访问权限,只能在同一个包中访问
public:最大的访问权限;
protected下方继承会讲
private是最小的访问权限,可以对类进行封装,public是最大的访问权限
图例:
![](https://img-blog.csdnimg.cn/img_convert/2ee5ae3cdc238dc938fbd3359cff9c1d.png)
1.1包
概念:Java当中组织类的一种方式
在java当中包就是对类,接口等的封装机制的体现,是一种对类或者接口很好的组织方式
白话:就是文件夹组织文件的
导入包中的类
三种导包方式:
1.
在java当中提供了很多现成的类供我们使用,可以使用import java.util.Date导入在Java.util这个包中的Date类
2.
import java.util.*; 这里的 * 就是通配符;
哪一个类调用,这里的 * 就导入哪个类的包
[建议]统配符 * 号非必要不要使用
3.
import static java.lang.Math. * //解释:导入一个静态的Math的方法
这样写,就可以不用加Math了,默认你会直接调用Math里的所有函数
直接可以写pow(),否则就需要Math.pow(),很少用
【注意事项】
在一个工程中,不允许存在相同名称的类,只要处在不同的包中即可
白话理解 : (在同一个文件夹中,不允许出现两个相同名字的文件)
总结:Java当中的包就是组织类的
和C语言当中的include<>导包方式差不多
2.自定义包:
在IDEA当中右键单击直接创建一个包
包名一般来说是公司的域名反写,比如说:com.wangyi.www
![](https://img-blog.csdnimg.cn/img_convert/eafddb05c19d5a08e82f5f4fcf7ab0da.png)
创建好之后:
![](https://img-blog.csdnimg.cn/img_convert/5ba99b96dd2e8e2f90151e784eaa0b62.png)
package:声明当前的java文件在哪个包当中
【注意事项】
在文件的最上方加上一个 package 语句指定该代码在哪个包中.
包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例com.wangyi.www)
包名要和代码路径相匹配. 例如创建 com.wangyi.www 的包, 那么会存在一个对应的路径 com/wangyi/www 来存储代码
如果一个类没有 package 语句, 则该类被放到一个默认包中.
3.static修饰成员变量
成员变量分为两种:
1.一种是普通成员变量
2.一种是被Static修饰过的成员变量:也称为类变量/静态成员变量,是所有对象公用的一个对象
共同点:在类的内部,在方法的外部
不同点:静态方法变量是被Static修饰了
静态成员的访问
Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的。
【静态成员变量特性】
1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
3. 类变量存储在方法区当中
4. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
类的静态成员变量,通过类名访问,也就是说:这个静态成员变量不属于对象
所以说new不new对象,和调用静态成员变量没有关系
class Student {
//private修饰的成员变量
private String name;
private String gender;
//静态变量
public static String classRoom = "1234";
public Student(String name, String gender) {
this.name = name;
this.gender = gender;
}
public void printStudent() {
//打印成员变量
System.out.println(this.name + "---" + this.gender);
}
}
public class Code3_21 {
public static void main(String[] args) {
//创建成员变量并给赋值
Student s1 = new Student("张三","男");
Student s2 = new Student("李四","女");
Student s3 = new Student("王麻子","男");
// 也可以通过对象访问:但是classRoom是上面三个对象共享的
// 使用对象去调用静态方法
//System.out.println(s1.classRoom);
//使用类名调用静态方法
System.out.println(Student.classRoom);
//调用s1成员方法
s1.printStudent();
}
}
静态方法的内部,是不能调用非静态的方法的.
因为静态方法不依赖于对象,但是非静态的方法依赖对象!
![](https://img-blog.csdnimg.cn/img_convert/13955b96fe15f5da4261e7394b1358e8.png)
【静态方法特性】
1. 不属于某个具体的对象,是类方法
2. 可以通过对象调用,也可以通过类名.静态方法名(...)方式调用,更推荐使用后者
3. 不能在静态方法中访问任何非静态成员变量
4.在静态方法内部不能使用this,静态的方法里面,只能用静态的属性
5.Static只能修饰成员变量不可以修饰局部变量
![](https://img-blog.csdnimg.cn/img_convert/b551f2e2416bb43cc74e3b5a254d684f.png)
4.静态成员的初始化
代码块:由{}括号括起来的就是代码块
静态代码块
static {
this.name = "zhangsan";
this.age = 12;
this.sex = "男";
System.out.println("调用实例化代码块");
}
实例(构造)代码块
构造块:定义在类中的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化
![](https://img-blog.csdnimg.cn/img_convert/956c56c674f225cfc82252e753c7a313.png)
代码示例:
class Student{
//实例成员变量
private String name;
private int age;
private String sex;
public Student() {
System.out.println("调用构造方法");
}
//实例代码块/构造代码块
{
this.name = "zhangsan";
this.age = 12;
this.sex = "男";
System.out.println("调用实例化代码块");
}
public void show(){
System.out.println("name: "+name+" age: "+age+" sex: "+sex);
}
}
public class Code3_21 {
public static void main(String[] args) {
Student st = new Student();//调用构造方法;
st.show(); //调用成员方法;
}
}
//执行结果:
// 实例化构造代码块
// 调用构造方法
// name: zhangsan age: 12 sex: 男
执行顺序:
![](https://img-blog.csdnimg.cn/img_convert/cfadc77a3e1e487789e16ced2c0cc28d.png)
普通代码块:
{
System.out.println("普通代码块,没有实际的意义");
}
![](https://img-blog.csdnimg.cn/img_convert/95ab86136065828979f4b0b509dedc99.png)
【注意事项】
1.静态代码块不管生成多少个对象,其只会执行一次(只要是调用类就会被执行)
2. 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
3. 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
4. 实例代码块只有在创建对象时才会执行
对象的打印
![](https://img-blog.csdnimg.cn/img_convert/ef5be8e832efc5d38a38a993508d2f7a.png)
也可以通过重写一个toString,来改变返回值
@Override//下章会介绍
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
public class Code3_21 {
public static void main(String[] args) {
Student st = new Student();//调用构造方法;
System.out.println(st);
}
}
此时返回的不会是第上图中的地址,而是
![](https://img-blog.csdnimg.cn/img_convert/a67854d84abc23393fa249540f4b8c7e.png)
二、继承
1.什么是继承
继承是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行 扩展,增加新功能,这样产生新的类,称 派生类。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。
图例:
![](https://img-blog.csdnimg.cn/img_convert/a0a19e7598d75e10f40de98d91c9a984.png)
![](https://img-blog.csdnimg.cn/img_convert/d17db64f4fa64982f553965917ca1836.png)
上述图示中,Dog和Cat都继承了Animal类,其中:Animal类称为 父类/基类或超类,Dog和Cat可以称为Animal的 子类/派生类,继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可。
创建一个类,继承父类,形成新的类 ,这时的子类对象由父类成员和子类成员构成
图例:
![](https://img-blog.csdnimg.cn/img_convert/bdd9c5b51539f9811dcf0fc28c46254c.png)
例如:狗和猫都是动物,那么我们就可以将共性的内容进行抽取,然后把共性放在父类当中,采用继承的思想来达到共用。主要用来解决共性的抽取,实现代码复用。
【注意事项】
1. 子类会将父类中的成员变量或者成员方法继承到子类中了
2.子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了
2.1白话理解:子类继承父类之后,如果没有特有的东西,继承父类是没有意义的.
3.在Java当中是不能实现多继承的
2.语法
修饰符 class 子类 extends 父类 {
// ...
}
可以使用extends 来继承父类
示例代码:
class An {
public int a = 999;
}
class Bn extends An { //子类Bn通过关键字extends继承父类An
public int b;
}
3.父类成员的访问:
class An {
public int a = 999;
}
class Bn extends An {
public int a = 111;
public int b;
public int c;
public void Eva() {
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
public class Date3_22 {
public static void main(String[] args) {
Bn b = new Bn(); //new一个子类类型的对象
b.Eva(); //通过对象去调用Eva方法访问子类成员方法
}
}
//输出结果为:
// 111
// 0
// 0
当父类和子类里面共同有一个成员变量时,会是怎么输出呢?
![](https://img-blog.csdnimg.cn/img_convert/315c209d409253ba6f7dfa238158156a.png)
此时我们发现:当父类和子类共有一个成员变量时,会优先输出子类自己的,那么我们如何去在子类当中调用父类的成员a呢?
【注意事项】
在子类方法中 或者 通过子类对象访问成员时:
1. 如果访问的成员变量子类中有,优先访问自己的成员变量。
2. 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
3. 如果访问的成员变量与父类中成员变量同名,则优先访问自己的
总结: 成员变量,自己有先访问自己的,没有再从父类中找;
4.super关键字:
在子类方法中,如果想要明确访问父类中成员时,借助super关键字即可
白话理解:来访问子类对象中从父类继承过来部分的一个引用
public void Eva() {
//通过用关键字super.a //来打印子类对象中从父类继承过来的a
System.out.println(super.a);
System.out.println(a); //输出子类中的a
System.out.println(c);
}
//此时输出结果为
// 999
// 111
// 0
作用:增加了代码的可读性:上面第二上代码解读:打印父类成员变量a;
【注意事项】
1.这里访问的是:子类对象中从父类继承过来部分, 这里没有产生父类对象,所以不是父类对象的引用!
2. 只能在非静态方法中使用
3. 在子类方法中,访问父类的成员变量和方法。
4.必须在子类当中使用,这样才能调用父类的属性或者方法
5.子类构造方法
概念:当父类有构造方法的时候,那么子类要先帮助我的父类进行构造,通过super()来进行构造
构造方法代码示例:
class An {
public int a;
public int b;
public An(int a, int b) { //构造方法
this.a = a;
this.b = b;
}
}
class Bn extends An {
public int c;
public Bn(int a, int b,int c) { //构造方法
super(a, b); //给父类的a,b赋值
this.c = c; //自己的c赋值
}
public void Eva() {
System.out.println(super.a);
System.out.println(b);
System.out.println(c);
}
}
public class Date3_22 {
public static void main(String[] args) {
Bn b = new Bn(11,22,33); //调用带三个参数的构造方法
b.Eva(); //通过对象去调用Eva方法访问子类成员方法
}
}
总结: 在构造子类对象时,先调用父类的构造方法,将父类继承部分构造完整,再调用子类自己,将子类新增成员初始化完整 (在帮助父类构造时,super必须放在第一行)
6.this 和super 的区别
【相同点】
1. 都是Java中的关键字
2. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
【不同点】
1. this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承 下来部分成员的引用
白话:一个是当前对象引用,一个是子类从父类继承下来部分成员的引用
2. 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
3. 在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同 时在构造方法中出现
4. 构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有
图例:
![](https://img-blog.csdnimg.cn/img_convert/a5ee1bd1534d9aee3e8bda2767284753.png)
7.再谈初始化
class Animal {
public String name;
public int age;
static { //静态代码块
System.out.println("Animal静态代码块!");
}
{ //构造代码块
System.out.println("Animal构造代码块!");
}
//构造方法必须和类名一致(可以构成重载),//不带参数的构造方法,构造方法
public Animal() {
System.out.println("不带参数的构造方法!Animal");
}
//带两个参数的构造方法
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
//成员方法
public void eat() {
System.out.println(this.name + " 正在吃饭!");
}
}
//继承了Animal的Dog类
class Dog extends Animal {
String fad;
static { //子类静态代码块
System.out.println("Dog静态代码块!");
}
{ //子类构造代码块
System.out.println("Dog构造代码块!");
}
//子类构造方法
public Dog() {
System.out.println("不带参数Dog!");
}
//带两个参数的子类构造方法
public Dog(String name, int age, String fad) {
super(name, age);
this.fad = fad;
}
//子类成员方法
public void bark() {
System.out.println(this.name + " 正在旺旺旺!!");
}
}
public class Code3_22 {
public static void main(String[] args) {
Dog dog = new Dog();
}
}
/*输出结果
Animal静态代码块!
Dog静态代码块!
Animal构造代码块!
不带参数的构造方法!Animal
Dog构造代码块!
不带参数Dog!
*/
图例解释 :
![](https://img-blog.csdnimg.cn/img_convert/5d7636f602f6a692150eb167094e2f2f.png)
上图代码执行顺序:
![](https://img-blog.csdnimg.cn/img_convert/fdce6f6b8ef64eca4c4749d6840c7b8c.png)
【注意事项】静态代码块只会执行一次
8.protected修饰限定符
![](https://img-blog.csdnimg.cn/img_convert/f18bcdd314175dbf9e1a0ff8286b2070.png)
9.final 关键字
fifinal关键可以用来修饰变量、成员方法以及类。
1. 修饰变量 为 表示常量(即不能修改)
final int a = 10; //被修饰过后为常量10
a = 20; // 编译出错 //常量不能被赋值
// 如果定义时没有被初始化
2. 修饰类:表示此类不能被继承
final public class Animal {
//...
}
public class Bird extends Animal {
//...
}
// 编译出错
10.继承和组合
例如电脑是由:控制器, 运算器 , 存储器 , 输入设备 , 输出设备构成。而这些都是它的功能,
把这些组合在一起就是一台电脑;这就是组合.
组合是通过对现有的对象进行拼装(组合)产生新的、更复杂的功能。因为在对象之间,各自的内部细节是不可见的,组合中一般都定义一个类型,所以在编译期根本不知道具体会调用哪个实现类的方法
继承和组合的优缺点:
![](https://img-blog.csdnimg.cn/img_convert/e2df9972e8ea7fcc0f3f5813b5644411.png)
如果没看懂请点击:深入理解Java中的组合和继承-HollisChuang's Blog
[继承小练]本题会输出什么?
class Base{
public Base(String s){
System.out.print("B");
}
}
public class Derived extends Base{
public Derived (String s) {
System.out.print("D");
}
public static void main(String[] args){
new Derived("C");
}
}
正确答案解析:http://t.csdn.cn/phHOV
三、多态
多态的概念:通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态。
同一件事情,发生在不同对象身上,就会产生不同的结果 。
白话理解:就是吃饭:狗吃狗粮,猫吃的是猫粮
1. 多态实现条件
在java中要实现多态,必须要满足如下几个条件,缺一不可:
1. 必须在继承体系下
2. 子类必须要对父类中方法进行重写
3. 通过父类的引用调用重写的方法
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。
1.1重写/覆盖(Override):
重写是子类对父类非静态、非private修饰,非fifinal修饰,非构造方法等的实现过程进行重新编写, 返回值和形参都不能改变。 即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要实现父类的方法
1.方法名称相同
2.参数列表相同
3.返回值类型相同
要点:子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致
代码示例:
class Animal {
public String name;
public void eat() {
System.out.println("正在吃饭");
}
}
class Dog extends Animal {
@Override //此时eat就构成了重载
public void eat() {
System.out.println(this.name + "正在吃狗粮");
}
}
public class Code3_23 {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "小狗";
dog.eat(); //输出结果是小狗正在吃狗粮
}
}
1.重写与重载(overload)对比:
1.方法名称相同
2.参数列表不同
3.返回值类型相同
【注意事项】
1. 被private修饰过的方法不能进行重写,因为private的权限是同一包中的同一类,而重写是在同一包 中的不同了类当中进行重写
2. 被static修饰过的方法不能进行重写,因为被static修饰过的方法它是类的,而重写是针对某个对象 的某个方法进行重写的
3. 子类的访问修饰限定符一定要大于等于父类的修饰限定符
4. 被final修饰过的方法不能进行重写, 被final修饰过的方法叫做【密封方法】
5. 构造方法不能被重写
6. 重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就 会编译报错, 提示无法构成重写. (提示的作用)
7. 被重写的方法返回值类型可以不同,但是必须是具有父子关系的(下方代码示例)
代码示例:
class Animal {
public String name; //定义父类成员
public Animal eat() { //返回类型是Animal类型
System.out.println("正在吃饭");
return null;
}
}
class Dog extends Animal {
public Dog(){
super();
}
@Override
public Dog eat() { //返回类型是Dog类
System.out.println(this.name + "正在吃狗粮");
return null;
}
}
1.2向上转型
实际就是创建一个子类对象,将其当成父类对象来使用。
白话理解:用父类引用 来 引用子类对象
代码示例:
Animal animal = new Dog();
//animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换。
图例1
![](https://img-blog.csdnimg.cn/img_convert/1ec5cb474e70514c8780694d93235dc8.png)
白话:计算机就是电子产品
图例2
![](https://img-blog.csdnimg.cn/img_convert/5a1fbb3d71de7e6ee31a78ac098448cc.png)
白话:狗就是一个动物
1.3运行时绑定/动态绑定:
![](https://img-blog.csdnimg.cn/img_convert/98d4cff2ff8411d9dd46678465a58c0f.png)
❀1.4如何发生向上转型
1. 直接赋值
图例
![](https://img-blog.csdnimg.cn/img_convert/e781f3923383b49d04dae942ea9e6c6a.png)
2. 方法传参
图例
![](https://img-blog.csdnimg.cn/img_convert/1c70f675f9914880c45ea00aff37aba5.png)
3. 方法返回
图例
![](https://img-blog.csdnimg.cn/img_convert/c1cd722d2128705ab74b97c8488d713d.png)
多态代码示例:
class Animal {
public String name;
public int age;
public void eat() { //返回类型是Animal类型
System.out.println("正在吃饭");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println(this.name+ " 正在吃狗粮!");
}
}
class Cat extends Animal {
public void eat() {
System.out.println(this.name + " 正在吃猫粮!");
}
}
public class Code3_23 {
//只有一个isFunc方法
//当animal这个引用所引用的对象不同时,所产生的的结果也不同
//这个就是多态
static void testFunc(Animal animal) {
animal.eat();
}
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println("System.out.println("分割线====================分割线");");
Cat cat = new Cat();
testFunc(cat);
}
}
//输出结果
![](https://img-blog.csdnimg.cn/img_convert/91084950a731ba536efbfc98ecc386b8.png)
【注意事项】
多态:
同一个引用,调用同一个方法,当这个引用所引用的对象不一样时,它在里面所呈现的结果也是不一样的!
前提:传过来的一定是子类
发生动态绑定需要满足两个条件:
1.发生了重写
2.是通过父类引用来调用这个重写的方法
以上就是多态
1.5向下转型
![](https://img-blog.csdnimg.cn/img_convert/429b51ebacc39a2e63f1b6ee66e6c120.png)
例子:
![](https://img-blog.csdnimg.cn/img_convert/75071a9eaffa5e602bf48580b44b4d00.png)
代码如下:且需要强转,且不安全
Cat cat=(Cat)new Animal();
代码小练习:验证年龄_牛客题霸_牛客网 (nowcoder.com)