java-learn(6):面对对象进阶(继承,多态,代码块,抽象类,接口,内部类)
static
被static修饰的成员变量,叫做静态变量
特点:
被刻类所有对象共享
不属于对象,属于类
随着类的加载而加载,优先与对象存在
调用方式:
1.类名调用:类名.变量
2.对象名调用
shift+alt+上下键: 可以上下移动这行的文字
javabean类:用来描述一类事物的类。比如student。teacher.
测试类:用来检查其它类是否书写正确,带有main方法的类,是程序的入口
工具类:不是描述一类事物的类。而是帮我们做一类事情的类。
工具类:
用static修饰方法 这样可以不用创建对象 直接用类名曲调用
工具类中一般都把构造方法私有化 这样可以防止创建工具类的对象 因为创建工具类没有什么意义
写一个工具类
import java.util.StringJoiner;
public class arrayUilt {
private arrayUilt(){}
public static String printArr(int[] arr){
StringJoiner ret = new StringJoiner(",","[","]");
for (int i = 0; i < arr.length; i++) {
ret.add(arr[i]+"");
}
return ret.toString();
}
public static Double average(double[] arr){
double ave = 0.0;
for (int i = 0; i < arr.length; i++) {
ave += arr[i];
}
return ave/arr.length;
}
}
static注意事项
static是java中的一个修饰符,可以修饰成员变量,成员方法
static修饰的是静态
静态方法只能访问静态方法和静态变量
非静态方法可以访问所有成员方法和成员变量
静态方法中没有this关键字,非静态有默认的this关键字
静态方法可以看做是共享的,它和对象无关,是随着类的加载而加载,可以通过类名进行调用
类的class字节码文件加载时,静态变量就加载到堆中的静态区,所以静态的不能调用非静态的
静态方法直接可以相互调用
重新认识主函数main
public static void main(String[] args){ }
public : 由jvm虚拟机调用,扩大访问权限 可以让虚拟机在外面找到并访问它 不管是否在同一个包中都能调用
static: 由jvm虚拟机调用,不用创建对象,直接类名调用
因为main方法是静态的,所以测试类中的方法也是静态的
void: 被jvm调用,不需要给jvm返回值
main:方法名 程序的主入口 一个通用的名称,虽然不是关键字,但是被jvm识别 其它名称不能识别
String[] args : 以前用于接收键盘录入数据的,现在没有用了 默认是空 什么也没有
继承
什么时候用继承
当类与类之间存在相同的内容时,并满足子类是父类中的一种,就可以考利使用继承,来优化代码
2.继承的格式
public class 子类 extends 父类 {};
继承特点?
子类可以得到父类的属性和行为,子类可以使用
子类可以在父类的基础上新增其它功能。
java只能单继承:一个类只能继承一个直接的父类
java支持多层继承
java中所有的类都直接或者间接继承object类
jvm会检测类是否有父类,如果没有就直接继承object
子类中只能访问父类中非私有的成员
子类到底能继承父类中哪些内容
构造方法:
不管是非私有还是私有的都不能继承 (构造方法的名称需要时类名)
成员变量:
1.非私有:能直接继承
2.私有的:能继承 但是不能直接用 只能通过成员方法去访问他,比如说getName()
成员方法:
在虚方法表中的能直接继承
不在虚方法表中的不能继承
(虚方法表:非private,非final,非static修饰的成员方法
子类会在直接父类的基础上添加自己的的虚方法)
继承中:成员变量的访问特点
是采用就近原则:
先在局部位置找,再到本类成员变量中找,再到父类中找,一层一层寻找,如果没有找到就会报错
访问局部位置的成员变量直接写变量名就行:name
访问本类中:this.name
访问父类中的: super.name
如果先访问间接父类的就要使用类型转换:(间接父类类名)this.name
继承中 成员方法的访问特点
方法重写的本质:子类重写的方法覆盖了虚方法表中的方法
1.子类方法的访问权限必须大于父类的访问权限
2.子类在返回值类型必须小于父类的
3.子类在重写父类的方法时,返回值,访问权限尽量与父类保持一致
4.只有能添加到虚方法表中的方法才能被重写
5.子类重写方法时,方法名必须与父类保持一致
@Override注释可以检查方法重写是否正确,并且可读性良好
小练习:
public class dog{
public void eat(){
System.out.println("吃狗粮");
}
public void drink(){
System.out.println("喝水");
}
public void lookhome(){
System.out.println("看家");
}
}
public class dog1 extends dog {
@Override
public void eat(){
}
public void attackhome(){
System.out.println("拆家");
}
}
public class dog2 extends dog{
@Override
public void eat(){
super.eat();
System.out.println("吃骨头");
}
}
public class dog3 extends dog{
@Override
public void eat(){
System.out.println("吃剩饭");
}
}
继承中 构造方法的访问特点
子类不能继承父类的构造方法,但是可以通过super调用
系统会默认的在子类的构造方法 调用super() 不管写不写都会执行 如果自己手动写需要把super写在第一行
在子类的构造方法中虚拟机会默认先访问父类的无参构造
如果想调用父类的有参构造需要自己手动写
this,super使用总结
this:相当于一个局部变量 储存当前调用者的地址值
super: 代表父类储存空间
this(…) 这可以访问本类其它的构造方法 如果在this中传入参数就相当于给那些变量给个默认值
多态
面对对象三大特征:封装 继承 多态
封装:对象代表什么 就得封装对应的数据 ,并提供对应数据的行为
认识多态
什么是多态:
对象的多种形态
多态的前提:
有继承或者实现关系
有父类引用指向子类对象
有方法的重写
多态的好处:使用多态可以接受所有的子类对象
多态中调用成员的特点
调用成员变量:编译看左边 运行也看左边
编译的时候会在父类中找看是否存在这个成员变量 如果不存在就直接报错 如果存在 就直接调用父类的成员变量
调用成员方法:编译看左边 运行看右边
编译的时候会在父类中找看时候存在这个成员方法 如果不存在直接报错 如果存在 就去子类的方法表中调用这个方法 所以如果子类重写了这个方法 就会直接用重写之后的方法了
成员变量不支持多态,成员方法支持多态
多态的优势和弊端
多态的优势:方法中,使用父类作为参数 可以接受所有子类对象
多态的弊端:不能使用子类的特有功能
引用数据类型的数据转换,有几种:
自动类型装换,强制类型装换
强制类型转化能解决什么问题:
可以转化为真正的子类,从而调用子类特有的功能
在转化过程中,可能会转换错误,要先用instanceof判断一下
先判断a是否为子类类型 如果是就直接转化为子类 并命名为b
如果不是放回false
if(a instanceof 子类 b){
b.子类方法();
}
多态的小练习

package duotai_practice;
public class person {
private String name;
private int age;
public person() {
}
public person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// public void keepPet(dog d,String somethid){
//
// }
//
// public void keepPet(cat c,String somethind){
//
// }
public void keepPet(animal a,String somethind){
// System.out.printf("年龄为%d岁的老王养了一只%s颜色的%d岁的狗\n",this.age,a.getColor(),a.getAge());
// a.eat(somethind);
if(a instanceof dog d){
System.out.printf("年龄为%d岁的老王养了一只%s颜色的%d岁的狗\n",this.age,d.getColor(),d.getAge());
d.eat(somethind);
}else if(a instanceof cat c){
System.out.printf("年龄为%d岁的老李养了一只%s颜色的%d岁的猫\n",this.age,c.getColor(),c.getAge());
c.eat(somethind);
}
}
}
package duotai_practice;
public class animal {
private int age;
private String color;
public animal() {
}
public animal(int age, String color) {
this.age = age;
this.color = color;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void eat(String something){
System.out.println("动物在吃" + something);
}
}
package duotai_practice;
public class dog extends animal{
public dog() {
}
public dog(int age, String color) {
super(age, color);
}
@Override
public void eat(String somethind){
System.out.printf("%d岁的%S颜色的狗两只前腿死死的抱住%s猛吃\n",this.getAge(),this.getColor(),somethind);
}
public void lookhome(){
System.out.println("看家");
}
}
package duotai_practice;
public class cat extends animal{
public cat(){
}
public cat(int age,String color){
super(age,color);
}
@Override
public void eat(String somethind){
System.out.printf("%d岁的%s颜色的猫咪这者眼睛侧着头吃%s\n",this.getAge(),this.getColor(),somethind);
}
public void catchMouse(){
System.out.println("逮老鼠");
}
}
测试类
package duotai_practice;
public class Test {
public static void main(String[] args) {
cat c = new cat(3,"白");
dog d = new dog(2,"黑");
person p1 = new person("老王",30);
person p2 = new person("老李",25);
p1.keepPet(d,"骨头");
p2.keepPet(c,"鱼");
}
}
包 final 权限修饰符 代码块
包
什么是包:
包就是文件夹,用来管理不同功能的java类
包名书写规范:
公司名反写+包作用 全部用小写,见名知意
什么叫全类名:
包名+类名
什么时候需要导包 什么时候不需要导包
在同一个包中不需要导包(会先在本包中寻找)
在java.lang中的类不需要导包
其它情况都要导包
当有两个包中相同名的类时,需要用全类名
final
final修饰类:是最终类不能被重写
final修饰方法:不能被修改
final修饰成员变量:变成常量 只能被赋值一次 之后就不能被修改了
常量的注意事项:
修饰基本数据类型:对应的值不能被改变
修饰引用数据类型:对应的地址值不能被改变 内部的属性值可以被改变
权限修饰符

注:无关系类就是没有继承关系的类
在实际开发应用中一般用private和public
成员变量使用private
成员方法用public
特殊情况:如果类中有方法是抽取其它方法中的共性,一般会私有
代码块
局部代码块:
提前结束变量的生命周期
构造代码块:
抽取所有构造方法中的重复代码
静态代码块:
属于静态的 随着类的加载而加载 只会被调用一次
一般在对一些数据初始化的时候使用
抽象类
抽象类的作用:
抽取共性时,无法确定方法体,就把方法体定义为抽象的
强制子类重写抽象方法
有抽象方法的是抽象类,抽象类不一样有抽象方法
抽象类和抽象方法的格式:
public abstract class 类名{ }
public abstract 返回类型 方法名(参数列表);
接口
接口的定义
public interface 接口名{}
接口与类时实现关系 用implement来表示
public class 类名 implement 接口名{ }
接口不能实例化
接口的子类(实现):
子类必须重写所有的抽象方法
或者子类自己本身是抽象类
注:子类可以同时实现多个接口 继承父类的同时也能实现多个接口接口
public class 类名 extends 父类 implement 接口1,接口2,接口3,…{}
接口中成员的特点:
成员变量:默认用public static final修饰 为常量
构造方法:接口中没有构造方法
成员方法:默认为public abstract 为抽象方法
jdk7以前只能定义抽象方法
接口与类之间的关系
类与类:继承关系 可以单继承 不能多继承 但是可以多层继承
接口与类:实现关系 一个类可以实现多个接口 但是需要重写所有的抽象方法
接口与接口:继承关系 可以单继承也可以多继承 如果实现类实现的是最小面的接口 那么需要重写所有的抽象方法
接口中的默认方法(jdk8之后添加的)
默认方法的作用:解决接口升级问题
就是如果接口中需要添加新的方法 为了避免让所有实现这个接口的类都要重写这个方法
在需要重写的时候再重写就行
默认方法要用default修饰
格式为: public default 返回类型 方法名(){}
,默认方法重写的时候要把default关键字去掉
如果有实现类实现了多个接口中有相同名字的默认方法 那么这个默认方法必须重写 如果不重写 在创建对象 调用这个默认方法时 会不知道是要调用哪一个
接口中的静态方法(jdk8之后添加的)
静态方法是不能被实现类重写的 因为静态方法不能被继承
调用的时候不能使用实现类名或者对象名去调用 只能使用接口名去调用 如果实现类中有相同方法名 用实现类名去调用 调用的是实现类中的方法
格式:public static 返回类型 方法名() { }
接口中的静态方法(jdk9之后添加的)
接口中的静态方法只为接口服务
格式:
private 返回类型 方法名(){};
为静态的方法服务的
private static 放回类型 方法名(){};
抽取一下共性 但是这个方法又不想被外界访问
public interface inter1 {
public static void start(){
System.out.println("开始");
log();
}
public static void end(){
System.out.println("结束");
log();
}
private static void log(){
System.out.println("记录日志的100行代码");
}
}
接口的应用
接口是规则,是行为的抽象 想要哪个类拥有这种行为 就实现这个接口就行
当一个方法的参数是一个接口时,可以传入所有实习的类 这就称为接口的多态 也遵守编译看左边 运行看右边
适配器设计模式
1.当一个接口中抽象方法过多时,但是又只需要用一部分的时候,就可以用适配器模式
2.书写步骤:
编写中间类XXXadopt,并空实现接口中的抽象方法
让真正的实现类去继承这个中间类,并重写需要用的方法
为了避免创建其它类创建适配器对象,一帮用abstract修饰适配器
内部类
初识内部类
类的五大成员:属性,方法,构造方法,代码块,内部类
什么是内部类:写在一个类中的类
什么时候用到内部类:属于外部类的一部分,但是单独存在没有意义
内部类中能使用外部类中的成员 包括私有的
外部类使用内部类中的成员时 需要创建对象
成员内部类:
内部类的成员方法的定义:
内部类可以用private,空着,protected,public 修饰 访问权限和外部类中的成员一样
在jdk16之前不能定义静态变量
获取内部类的两种方法:
1.在外部类中设置返回内部类对象的方法 接收时可以用内部类的父类 如果实在找不到就用共同点的父类object
2.外部类.内部类 名称 = new 外部类().new 内部类();
当内部类和外部类成员重名的时候可以用 外部类.this.成员 来调用外部类成员 this.成员 调用内部类中的成员 this关键字记录的是类实例之后的地址
Outer.this 是编译器自动给内部类中添加的
静态内部类:
静态内部类也属于成员内部类的一种
静态内部类只能访问外部类的静态成员
要访问非静态的需要创建外部类对象
获取静态内部类中的方法:
非静态方法: 先创建静态内部类,再调用方法 Outer.Inner in = new Outer.Inner();
调用静态方法:外部类.内部类.方法名;
局部内部类:
static public protect private只能修饰成员变量 不能修饰局部变量
局部内部类: 定义在方法中的类
可以访问方法中的变量和外部类的中成员
外界无法使用,必须在方法内部创建对象使用
匿名内部类:
1.什么是匿名内部类:隐藏了名字的内部类,可以写在局部位置,也可以写在成员位置。
2.匿名内部类的格式:
new 类名或者接口名(){};
匿名内部类的主体是{}中的内容
类名或者接口名代表继承这个类或者实现这个接口
()代表的是空参构造
new 代表的是创建一个对象
;代表结束
3.格式的细节:
1.继承或者实现 方法重写 创建对象
整体就是一个类的子类对象或者接口的实现类对象
使用场景:
当参数是接口或者类时,
以接口为例,可以传递这个接口的实现类对象,如果实现类只要使用一次,就可以使用匿名内部类简化代码。
更多推荐



所有评论(0)