java基础9-面向对象进阶
1.static静态变量
- 被static修饰的成员变量,叫做静态变量
特点:被该类所有对象共享;不属于对象属于类;随着类的加载而加载的,优先于对象存在
调用方式:类名调用、对象名调用
- 被static修饰的成员方法,叫做静态方法
特点:多用在测试类和工具中; javabean类中很少会用
调用方式:类名调用;对象名调用
static的注意事项:
静态方法只能访问静态变量和静态方法
非静态方法可以访问静态变量或静态方法,也可以访问非静态的成员变量和非静态的成员方法
静态方法中是没有this关键字
javabean类:用来描述一类事务的类。比如,Student,Teacher,Dog,Cat等。
测试类:用来检查其他类是否书写正确,带有main方法的类,是程序的入口。
工具类:不是用来描述一类事物的,而是帮我们做一些事情的类
main方法:
public:被JVM调用,访问权限足够大;
static:被JVM调用,不用创建对象,直接类名访问;因为main方法是静态的,所以测试类中其他方法也需要是静态的
void:被JVM调用,不需要给JVM返回值
main:一个通用的名称,虽然不是关键字,但是被JVM识别
2.工具类
- 类名见名知意
- 私有化构造方法
- 方法定义为静态
按照以下要求编写一个数组的工具类:ArrayUtil
- 提供一个工具类方法printArr,用于返回整数数组的内容。
返回的字符串格式如:[10,20,50,34,100](只考虑整数数组,且只考虑一堆数组)
- 提供这样一个工具方法getArrage,用于返回平均分(只考虑浮点型数组,且只考虑一堆数组)
- 定义一个测试类TestDemo,调用该工具类的工具方法,并返回结果。
示例代码:
public class TestDemo { public static void main(String args[]){ int[] arr1={1,2,3,4,5}; String str=ArrayUtil.printArr(arr1); System.out.println(str); double arr[]={2.2,3.4,2.5,6.7}; double avg=ArrayUtil.getAverage(arr); System.out.println(avg); } } public class ArrayUtil { private ArrayUtil() {} public static String printArr(int arr[]){ StringBuilder sb = new StringBuilder(); sb.append("["); for(int i = 0; i < arr.length ; i++){ if(i==arr.length-1)sb.append(arr[i]); else sb.append(arr[i]+", "); } sb.append("]"); return sb.toString(); } public static double getAverage(double[] arr){ double sum=0; for(int i = 0; i<arr.length; i++){ sum+=arr[i]; } return sum/arr.length; } }
3.封装 使用关键字extends
public class Student extends Person{} //Student称为子类(派生类),Person称为父类(基类或超类)
使用继承可以把多个子类中重复的代码抽取到父类中,提高代码的复用性。
子类可以在父类的基础上,增加其他功能,使子类更强大。
特点:Java只支持单继承,不支持多继承,但支持多层继承
单继承:一个子类只能继承一个父类
不支持多继承:子类不能同时继承多个父类
多层继承:子类A继承父类B,父类B可以继承父类C
每一个类都直接或间接的继承于Object
- 构造方法:父类的构造方法不能被子类继承
public class TestDemo { public static void main(String args[]) { Zi z1 = new Zi();//不报错因为类中会自动添加空参构造方法 Zi z2 = new Zi("zhangsan",23);//会报错 } } class Fu{ String name; int age; public Fu() {} public Fu(String name,int age){ this.name=name; this.age=age; } } class Zi extends Fu{ }
子类中所有的构造方法默认先访问父类中的无参构造,再执行自己
子类构造方法的第一行语句默认是:super(),不写也存在
- 成员变量:子类能够继承父类的成员变量,但是父类中被private修饰的不能直接使用,需要通过get和set方法使用
访问特点:就近原则,谁离我近我就用谁//先局部再本类,然后父类,逐级向上
System.out.println(name); System.out.println(this.name); System.out.println(super.name);
- 成员方法:只有父类中的虚方法才能被子类继承
(从最顶级的父类开始设计虚方法表,二级父类在顶级的基础上加上自己的虚方法表,在继承关系中顺延)虚方法:非private,非static,非final
访问特点:直接调用满足谁离我近我就用谁;super调用直接访问父类
4.方法的重写(注解@Override):当父类的方法不能满足子类现在的需求时,需要进行方法重写
书写格式:在继承体系中,子类出现和父类一模一样的方法声明,我们就称子类这个方法是重写的方法。方法的重写会覆盖虚方法表中的方法
重写注意事项和要求:
- 重写方法的名称、形参列表必须与父类中的一致。
- 子类重写父类方法时,访问权限子类必须大于等于父类(空着不写<protectes<public)
- 子类重写父类方法时,返回值类型子类必须小于等于父类
- 建议:重写的方法尽量和父类保持一致
- 只有被添加到虚方法表中的方法才能被重写
5.多态 同类型的对象,表现出的不同形态
表现形式: 父类类型 对象名称 = 子类对象;
多态的前提:有继承关系;有父类引用指向子类对象(例如Fu f = new Zi(); );有方法重写
调用成员方法:编译看左边,运行看右边(javac编译代码的时候,会看左边的父类中有没有这个方法,如果有编译成功,反之编译失败。java运行代码的时候,实际上运行的是子类中的方法。)
调用成员变量:编译看左边,运行也看左边
多态的优势:
(1) 在多态形势下,右边对象可以实现解耦合,便于扩展和维护。
Person p = new Student();//当需要时可以改成Teacher(); p.work;//业务逻辑发生改变时,后续代码无需修改
(2)定义方法的时候,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利。
弊端:
public class TestStudent { public static void main(String[] args){ Animal a = new Dog(); a.eat();//编译看左边,运行靠右边 //多态的弊端:不能调用子类的特有功能 //报错原因:调用成员方法时编译看左边,运行看右边 //编译的时候检查左边的父类中是否有该方法,没有则直接报错 a.lookHome();//报错
//解决方案 变回子类型
//Dog d = (Dog)a;
//d.lookHome();
} } class Animal{ public void eat(){ System.out.println("动物在吃东西"); } } class Dog extends Animal{ @Override public void eat(){ System.out.println("狗吃骨头"); } public void lookHome(){ System.out.println("狗看家"); } } class Cat extends Animal{ @Override public void eat(){ System.out.println("猫吃鱼"); } }
a instancdof Dog: a是不是Dog类型
新特性为 a instancdof Dog d: 先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d; 如果不是,则不强转,结果直接是false
引用数据类型的类型转换,有几种方式? 自动类型转换,强制类型转换
强制类型转换能解决什么问题?
- 可以转换成真正的子类类型,从而调用子类独有功能。
- 转换类型与真实对象类型不一致会报错。
- 转换的时候用instanceof关键字进行判断
6.包和final
包就是文件夹。用来管理各种不同功能的Java类,方便后期代码维护。
包名的规则:公司域名反写+包的作用,需要全部英文小写,见名知意。
package com.cnblogs.domain.Student;//全类名、全限定名
使用其他类的规则:
使用同一个包中的类时,不需要导包。
使用java.lang包中的类时,不需要导包。
其他情况都需要导包。
如果同时使用两个包中的同名类,需要用全类名(包名+类名)。
- final 是最终的,不可以被改变的。
用final修饰一个方法:表明该方法是最终方法,不能被重写;用final修饰一个类:表明该类是最终类,不能被继承;用final修饰一个变量:叫做常量,只能被赋值一次。
- 常量 命名规范:单个单词全部大写;多个单词:全部大写,单词之间用下划线隔开
注:final修饰的基本变量是基本类型,那么变量存储的数据值不能发生改变; final修饰的变量是引用类型,那么变量存储的地址值不能发生改变,对象内部的可以改变。
- 权限修饰符: 是用来控制一个成员能够被访问的范围的, 可以修饰成员变量,方法,构造方法,内部类。
四种作用范围由小到大:private < 空着不写(default)< protected < public

权限修饰符的使用规则:实际开发中一般只用private和public,成员变量私有,方法公开
特例:如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有
- 构造代码块:写在成员位置的代码块
作用:可以把多个构造方法中重复的代码抽取出来
执行时机:我们在创造本类对象的时候会先执行构造代码块再执行构造方法
- 静态代码块: 格式:static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次
使用场景:在类加载的时候,做一些数据初始化的时候使用
static{ System.out.println("静态代码块执行");//只执行一次 }
7.抽象类和抽象方法: 抽象方法子类必须强制重写
抽象方法:将共性的行为(方法)抽取到父类之后。由于每一个子类执行的内容是不一样的,所以,在父类中不能确定具体的方法体。该方法就可以定义为抽象方法。
抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类。
定义示例:
public abstract class Person {//抽象类 public abstract void work();//抽象方法 }
注意事项:
(1) 抽象类不能实例化(不能创建对象)
(2) 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
(3) 可以有构造方法
(4) 抽象类的子类要么重写抽象类中的所有抽象方法;要么是抽象类
public abstract class Animal { private String name; private int age; public Animal() { } public Animal(String name, int age) { this.name = name; this.age = age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public int getAge() { return age; } public void drink(){ System.out.println("动物在喝水"); } public abstract void eat(); }
public class Frog extends Animal{ public Frog() { } public Frog(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("青蛙在吃虫子"); } }
8.接口 接口是一种规则,是对行为的抽象
- 接口用关键字interface来定义 public Interface 接口名{}
- 接口不能实例化
- 接口和类之间是实现关系,通过implements关键字表示 public class 类名 implements 接口名{}
- 接口的子类(实现类):要么重写接口中的所由抽象方法,要么是抽象类
- 实现一个接口要重写里面所有的抽象方法
注意1:接口和类的实现关系,可以单实现,也可以多实现。
public class 类名 implements 接口名1,接口名2{}
注意2:实现类还可以在继承一个类的同时实现多个接口。
public class类名extends父类 implements接口名1,接口名2{}
public class Java01 { public static void main(String[] args) { Frog f= new Frog("小青", 1); System.out.println(f.getName()+", "+f.getAge()); f.eat(); f.swim(); Rabbit r = new Rabbit("小白",2); System.out.println(r.getName()+", "+r.getAge()); r.eat(); } }
ublic abstract class Animal { private String name; private int age; public Animal() { } public Animal(String name, int age) { this.name = name; this.age = age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public int getAge() { return age; } public void drink(){ System.out.println("动物在喝水"); } public abstract void eat(); }
public interface Swim { public abstract void swim(); }
public class Rabbit extends Animal{ public Rabbit() { } public Rabbit(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("兔子吃胡萝卜"); } }
public class Frog extends Animal implements Swim{ public Frog() { } public Frog(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("青蛙在吃虫子"); } @Override public void swim() { System.out.println("青蛙在游泳"); } }
public class Dog extends Animal implements Swim{ public Dog() { } public Dog(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("狗在吃骨头"); } @Override public void swim() { System.out.println("狗在游泳"); } }
接口中成员的特点:
- 成员变量 只能是常量 默认修饰符:public static final
- 无构造方法
- 成员方法 只能是抽象方法 默认修饰符 public abstract
接口和类之间的关系:
- 类和类的关系:继承关系,只能单继承,不能多继承,但是可以多层继承
- 类和接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口。
- 接口和接口的关系:继承关系,可以单继承,也可以多继承
JDK8后接口新增方法:
(1) 允许在接口中定义默认方法,需要使用关键字default修饰(作用:解决接口升级的问题)
默认方法定义格式:public default 返回值类型 方法名(参数列表){}
范例:public default void show (){}
注意事项:
- 默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字。
- public可以省略,default不能省略
- 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写。
(2) 允许在接口中定义静态方法,需要用static修饰。
静态方法的定义格式:public static 返回值类型 方法名(参数列表){}
范例:public static void show(){}
注意事项:
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,static不能省略
JDK9后接口新增方法:
(3) 接口中私有方法的定义格式:
格式1:private 返回值类型 方法名(参数列表){} //为默认方法服务(普通的私有方法)
范例1:private void show(){}
格式2:private static 返回值类型 方法名(参数列表){} //为静态方法服(静态的私有方法)
范例2:private static void method(){}
接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了。
当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态。
适配器:解决接口与接口实现类之间的矛盾问题
当一个接口中抽象方法过多,但是只需要使用其中一部分的时候就可以适配器设计模式
书写步骤:(1) 编写中间类XXXAdapter实现对应的接口,对接口中的抽象方法进行空实现
(2) 让真正的实现类继承中间类,重写需要用的方法
(3) 避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰
public interface Inter { public abstract void method1(); public abstract void method2(); public abstract void method3(); public abstract void method4(); public abstract void method5(); }
public class InterImpl extends InterAdapter{ //需要哪个方法就重写哪个方法 @Override public void method5() { System.out.println("只要用第五个方法"); } }
public abstract class InterAdapter implements Inter{//定义为空方法 @Override public void method1() { } @Override public void method2() { } @Override public void method3() { } @Override public void method4() { } @Override public void method5() { } }
9.内部类:成员内部类/ 静态内部类/ 局部内部类/ 匿名内部类
在一个类的里面再定义一个类,里面的类叫内部类
内部类表示的事务是外部类的一部分;内部类单独出现没有任何意义
内部类的访问特点:
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
//写一个javabean类描述汽车 //属性:汽车品牌、汽车年龄、汽车颜色、发动机品牌、使用年限 public class Car {//外部类 String carName; int carAge; int carColor; class Engine{//内部类(成员内部类) String engineName; int engineAge; } }
成员内部类
获取成员内部类的对象:(1) 在外部类中编写方法,对外提供内部类对象
(2)直接创建格式: 外部类名.内部类名 对象名 = 外部类对象.内部类对象;
示例1:
//方法一 public class Outer{ String name; class Inner{ } } public class Test{ public static void main(String args[]) { Outer.Inner oi = new Outer().new Inner(); } } //方法二 public class Outer{ String name; private class Inner{ } public Inner getInstance(){ return new Inner(); } } public class Test{ public static void main(String args[]) { Outer o = new Outer(); o,getInstance(); } }
示例2:
public class Outer { private int a = 10; class Inner { private int a = 20; public void show() { int a=30; System.out.println(a);//30 System.out.println(this.a);//20 System.out.println(Outer.this.a); } } } public class TestDemo { public static void main(String args[]) { Outer.Inner oi = new Outer().new Inner(); oi.show(); } }
静态内部类(也是成员内部类中的一种)
静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象。
格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
调用非静态方法的格式:先创建对象,用对象调用。
调用静态方法的格式:外部类名.内部类名.方法名();
public class TestDemo { public static void main(String args[]) { //创建静态内部类的对象 只要是静态的东西,都可以用类名点直接获取 Outer.Inner oi = new Outer.Inner(); oi.show(); //静态方法 Outer.Inner.show2(); } } public class Outer { int a = 10; static int b = 20; static class Inner { public void show() { Outer o =new Outer(); System.out.println(o.a); System.out.println(b); } public static void show2() { System.out.println("show2"); } } }
局部内部类
- 将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。
- 外界无法直接使用,需要在方法内部创建对象并使用。
- 该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
public class TestDemo { public static void main(String args[]) { Outer o = new Outer(); o.show(); } } public class Outer { int b = 20; public void show(){ int a = 10; class Inner{//局部内部类 String name; int age; public void method1(){ System.out.println(a); System.out.println(b); System.out.println("method1"); } public static void method2(){ System.out.println("method2"); } } Inner i = new Inner(); System.out.println(i.name); System.out.println(i.age); i.method1(); Inner.method2(); } }
匿名内部类:隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置
格式:
new 类名或者接口名(){
重写方法;//包含了继承或实现;方法重写;创建对象(整体就是一个类的子类对象或者接口的实现类对象)
}
使用场景:当方法的参数是接口或者类时,以接口为例,可以传递这个接口的实现类对象。如果实现类只要使用一次,就可以通过匿名内部类简化代码
public class TestDemo { public static void main(String args[]) { new Swim(){ //没有名字的类是大括号部分 括号中是对Swim接口的实现 new创建该类的对象 @Override public void swim() { System.out.println("重写了游泳的方法"); } }; method( new Animal(){ //没有名字的类是大括号部分 括号中是animal的子类 new创建该类的对象 @Override public void eat() { System.out.println("重写了eat的方法"); } } ); } public static void method(Animal a){//Animal a = 子类对象 多态 a.eat();//编译看左边,运行看右边 } }

浙公网安备 33010602011771号