面相对象
基础
定义
类是共同特征的描述、对象是真实存在的具体实例
用来描述一类事物的类叫javabean类,javabean类中不写main方法,编写main方法的类叫测试类,可以在测试类中创建javabean类的对象并赋值调用
一个java文件中可以定义多个class类,而且只能有一个是public修饰,public修饰的类名必须成为代码文件名;事迹开发中开始建议一个文件定义一个class类
public class 类名{
成员变量:修饰符 数据类型 变量名称 = 初始化值;一般不指定,存在默认值
成员方法
构造器
代码块
内部类
}
数据类型 | 细明 | 默认值 |
---|---|---|
基本类型 | byte short int lang | 0 |
基本类型 | float double | 0.0 |
基本类型 | boolean | false |
引用类型 | 类,接口,数组,String | null |
引出案例
public class phone {
String brand;
double price;
public void call(){
System.out.println("手机在打电话");
}
public void playGame(){
System.out.println("手机在玩游戏");
}
}
public class phoneT {
public static void main(String[] args) {
phone p = new phone();
p.brand = "小米";
p.price = 1999;
System.out.println(p.price); //1999.0
p.call(); //手机在打电话
}
}
public class girlFriend {
String name;
private int age;//针对每一个私有化的成员变量提供get(对外提供成员变量的值)和set(给成员变量赋值)方法
private String gender;
public void setAge(int age) {
if (age > 18 & age < 25) {
this.age = age;
} else {
System.out.println("非法参数");
}
}
public int getAge() {
return age;
}
public void setGender(String gender) { //方法里的是局部变量,类里的是成员变量,同名变量可用
this.gender = gender; //没有this的话就是就近原则
}
public String getGender() {
return gender;
}
public void eat() {
System.out.println("在吃东西");
}
public void sleep() {
System.out.println("在睡觉");
}
//如果我们没有写任何构造方法,虚拟机会给我们自动加一个空参构造方法
public girlFriend(){
System.out.println("空参构造方法");
}
//有参构造
public girlFriend(String name,int age){
this.name = name;
this.age = age;
}
}
public class grilFrientT {
public static void main(String[] args) {
girlFriend g1 = new girlFriend(); //空参构造方法
g1.name = "小老虎";
g1.setAge(22);
System.out.println(g1.getAge()); //22
g1.eat(); //在吃东西
//调用的空参构造
girlFriend g2 = new girlFriend(); //空参构造方法
girlFriend g3 = new girlFriend("小狮子",18);
System.out.println(g3.getAge()); //18
System.out.println(g3.name); //小狮子
}
}
标准javabean类
用来描述一类事物的类
类名见名知意,成员变量用private修饰,提供至少两个构造方法空参全参,成员方法提供每一个成员变量的getset方法
快捷键
alt + insert
alt + fn + insert
构造方法
也叫构造器,构造函数,在创建对象的时候给成员变量进行初始化的
public class Student{
修饰符 类名(参数){
方法体;
}
public Student(){
//空参构造方法,初始化对象时成员变量的数据均采用默认值
}
public Student(String name,int age){
//带全部参数构造方法
this.name = name
//测试类就能直接赋值new Student("zs",12)
}
}
方法名与类名相同,没有返回值类型,void也没有,没有具体的返回值,不能写return;
创建对象的时候由虚拟机调用,不能手动调用构造方法,每创建一次对象,就会调用一次构造方法;
如果没有定义构造方法,系统会给出一个默认的无参构造方法,定义了构造方法,系统不再提供默认的构造方法;
构造方法的重载:
带参构造方法和无参构造方法两者方法名相同,但是参数不同,叫构造方法的重载
无论是否使用都要写上无参和带全部参数的构造方法
构造方法是创建对象的时候,虚拟机会自动调用构造方法,给成员变量进行初始化
成员变量和局部变量
区别 | 成员变量 | 局部变量 |
---|---|---|
类中位置不同 | 类中,方法外 | 方法内,方法申明上 |
初始化值不同 | 有默认的初始化值 | 使用之前要赋值 |
内存位置不同 | 堆内存 | 栈内存 |
生命周期不同 | 随对象创建而存在,随对象消失而消失 | 随着方法的调用存在,方法运行结束后消失 |
作用域 | 整个类中有效 | 当前方法中有效 |
包
就是文件夹,用来管理各种不同功能的java类
命名规则:公司域名反写+包的作用,全英文,见名知意:com.ithaime.domain
package com.ithaime.domain
public class Student{私有化成员变量,构造方法,成员方法}
使用其他类时需要用全类名(包名+类名)
public class Test{
psvm{
com.ithaime.domain.Student s = new com.ithaime.domain.Student();
}
}
导包:
import com.ithaime.domain.Student;
public class Test{
psvm{
tudent s = new Student();
}
}
使用同一个包中的类时不需要导包,使用java.lang中的类时不需要导包,其他情况需要导包;如果同时使用两个包中的同名类,需要用全类名
权限
权限修饰符
作用范围由小到大:private<缺省/默认<protected<public
权限修饰符 | 同一个类中 | 同一个包中其他类 | 不同包下的子类 | 不同包下的无关类 |
---|---|---|---|---|
private | 1 | |||
空着不写 | 1 | 1 | ||
protected | 1 | 1 | 1 | |
public | 1 | 1 | 1 | 1 |
一般都是成员变量私有,方法公开;如果方法中的代码是抽取其他方法中的共性代码,这个方法一般也私有
抽象类和抽象方法
作用:
- 抽取共性时,无法确定方法体,就把方法定义为抽象的强制让子类按照某种格式重写
子类没有重写的话没办法,那就别写方法体了,用一个抽象关键字修饰
抽象方法子类必须强制重写,并按父类的格式(统一)
如果一个类中存在抽象方法,该类就必须声明为抽象类
public abstract class 类名{
public abstract 返回值类型 方法名(参数);
}
特点:
-
抽象类不能实例化
-
抽象类不一定有抽象方法,有抽象方法一定是抽象类
-
可以有构造方法;创建子类对象时给属性赋值
- 抽象类是父类的话就要把子类中的共同方法抽取上来,创建子类对象时给共有属性赋值
-
抽象类的子类要么重写抽象类中的所有抽象方法,要么子类本身是抽象类
- 但是子类也是抽象类的话也不能创建实例对象,要创建对象还要写一个孙子类再继承子类重写所有抽象方法
示例:
public abstract void work();
子类继承后重新里面的所有抽象方法
public void work(){
System.out.print("学习")
}
没有方法体的方法
抽象类不能创建对象
设计的时候先正常写,当有抽象方法时再把父类变成抽象类
- 意义
算是一个标准,其他人实现方法要统一【强制子类按照父类的格式进行重写】
static
静态变量是所有对象共享的
修饰成员变量(静态变量):被该类所有对象共享,不属于对象;属于类;静态变量是随类的加载而加载的,优于对象出现
类名调用(每个类都有),对象名调用
修饰成员方法(静态方法):多用在测试类和工具类中,javabean中很少用到,类名调用,对象名调用
有一个专门的静态存储位置(静态区),在堆内存里,每一个new出来的都和静态区的内容连起来
例子:
新增一个公共类:共同的老师
public String teacherName
给对象赋值:
s1.teacherName = "zs" // zs
s2没操作 // null
public Static String teacherName
s1.teacherName = "zs" // zs
s2没操作 // zs
也能直接调用
Student.teacherName = "zs" //s1和s2都能获得
- Static的注意事项
- 静态方法只能访问静态变量和静态方法
- 非静态变量可以访问静态变量或静态方法,也可以访问非静态的成员变量和非静态的成员方法
- 静态方法没有this关键字
非静态成员方法的形参中有一个隐藏的this,调用方法的时候虚拟机赋值的,表示当前方法调用者的地址值
调用非静态的成员方法时前面也有一个隐藏的this
静态方法不能调用非静态的成员变量,因为没有this关键字
静态方法不能调用实例变量
静态方法不能调用非静态的成员方法
-
main方法
- public:被JVM调用,访问权限足够大
- static:被JVM调用,不用创建对象,直接类名访问。因为main方法是静态的,所以测试类中其他方法也需要是静态的。
- void:被JVM调用,不需要给JVM返回值
- main:一个通用的名称,虽然不是关键字,但是被JVM识别
- String[] args:以前用于接收键盘录入数据的,现在没用
-
测试类
用来检查其他类是否正确,带有main方法的类,是程序的入口
- 工具类--静态方法的使用
不是用来描述一类事物的,用来帮助我们做一些事情的类
类名见名知意,私有化构造方法(为了不让外界创建),方法定义为静态(方便调用)
public class ArrUtil{
private ArrUtil(){} //私有化构造方法
public static int getMax(){...}
public static int getMin(){...}
public static int getSum(){...}
public static int getAvg(){...}
}
例子:
public class ArrayUtil(){
private ArrayUtil(){}
public static double getAverage(double[] arr){
double sum = 0;
for (int i = o; i < arr.length;i++){
sum = sum+arr[i]
}
return sum/arr.lengrh;
}
}
main:
double[] arr = {1.1,2.2};
double avg = ArrayUtil.getAverage(arr);
sout:avg
关键字
private关键字
权限修饰符,可以修饰成员(成员变量,成员方法),被private修饰的成员只能在本类中才能访问
用public修饰getset方法
就近原则和this关键字
public class G{
private int age;
public void method(){
int age = 10
sout:age //10就近原则
sout:this.age // 0,直接在成员位置找
this.age = age // 把测试类中调用方法传递过来的age给成员位置的age
}
}
this的内存原理:
this区分局部变量和成员变量
this本质是所在方法调用者的地址值
new出来的都在堆内存里,方法依次进栈
对象内存图
从JDK8开始,取消方法区,新增元空间.
把原来方法区的多种功能进行拆分
有的功能放到了堆中,有的功能放到了元空间中。
-
一个对象
- 加载class文件
- 申明局部变量
- 在堆内存中开辟一个空间
- 默认初始化
- 显示初始化
- 构造方法初始化
- 将堆内存中的地址值赋值给左边的局部变量
对堆空间的对象来讲,没有变量指向这个空间,这个空间就会消失变成垃圾
Final关键字
修饰方法:不能被重写,最终方法,父类写show()后子类不能重写
public final void show(){}
修饰类:最终类,不能被继承
final class Fu{}
修饰变量:常量,只能被赋值一次
final int a = 1;
常量:一般作为系统的配置信息,方便维护,提高可读性,单个单词全部大写,多个单词全部大写用下划线隔开;
final修饰的变量是基本类型,变量存储的数据值不能发生改变。
final修饰的是引用类型,地址值不可变,对象内部的可以改变;属性值等
字符串是不可变的:
private final
继承关键字super
this就是一个局部变量,方法被调用的时候就有值
super代表父类存储空间
//需求:默认为ch
public stu(){
//调用本类其他构造方法,虚拟机就不会再添加super()
this(null.0."ch")
}
public stu(String name,int age,String school){
super();
this.name = name;
this.age = age;
this.school = school;
}
继承关键字extends
子类重复的代码抽取到父类(基类或超类)中,提高代码复用性,子类(派生类)还可以增加其他功能,使子类更强大
类与类之间存在相同的内容,并且满足子类是父类的一种,就可以考虑使用继承
public class Student extends Person{} //Student是子类,Person是父类
封装
对象代表什么,就得封装对应的数据,并提供数据对应的行为。
继承
- 特点
java只支持单继承(一个子类只能继承一个父类),不支持多继承(子类不能同时继承多个父类),但支持多层继承
每个类都直接或间接继承Object
public class A {} ==> public class A extends Object //虚拟机加的
子类只能访问父类中非私有的成员
- 子类能继承父类中哪些内容
构造方法 | public 不能 | private 不能 |
---|---|---|
成员变量 | public 能 | private 能 |
成员方法 | public 能 | private 不能 |
成员变量可以被继承下来,但是不能调用
构造方法:public修饰的类要跟文件名保持一致
成员变量的访问特点
就近原则,现在局部位置找,本类成员位置找,父类成员位置找,逐级往上
出现重名变量:
sout:name
sout:this.name
sout:super.name
继承中成员方法的访问特点
虚方法表:非private,非static,非final
虚方法才能被继承
继承是在父类基础上添加自己类中的虚方法
方法重写
继承体系中,子类出现了和父类中一模一样的方法声明,就称子类的这个方法时重写的方法@Override
子类覆盖了从父类中继承的虚方法表中的方法
方法重写的本质是覆盖虚方法表中的方法
方法重写:
- 重写方法的名称,形参列表必须与父类中的一致
- 子类重写父类方法时,访问权限子类必须大于等于父类【缺省<protected<public】
- 子类重写父类方法时,返回值类型子类必须小于等于父类
- 私有方法不能被重写
- 子类不能重写父类的静态方法
- 只有被添加到虚方法表中的方法才能被重写
- 重写的方法尽量与父类保持一致
构造方法的访问特点
有一个虚拟机自动默认添加的无参构造
- 父类的构造方法不会被子类继承
- 子类的所有构造方法默认先访问父类中的无参构造,再执行自己
子类默认有一个隐藏的super()
方法在最上面调用父类的无参构造
- 子类不能继承父类的构造方法,但是可以通过
super
调用 - 子类构造方法的第一行有一个默认的
super();
- 默认先访问父类中无参构造,再执行自己
- 想要有参的父类构造,必须手动写
多态
概述
多态是同类型的对象表现出的不同形态【对象的多种形态】
方法的参数是一个类,此时就可以传递这个类所有的子类对象
- 前提:
有继承/实现关系
有父类引用指向子类对象
有方法重写
- 好处:
使用父类作为参数可以接收所有子类对象,有扩展性
父类类型 对象名称 = 子类对象;
学生形态 | 对象 |
---|---|
Student s = | new Student() |
人的形态 | 对象 |
People p = | new Student() |
Fu f = new Zi();
//Teacher Student Administrator
public void register(Person p){
p.show() // 根据传递对象的不同调用不同的show方法
}
例子:
People-Student、Teacher
Test
public class People {
private String name;
private int age;
public People() {
}
public People(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 show(){
System.out.println(name+","+age);
}
}
public class Student extends People{
public void show(){
System.out.println("学生的信息"+getName()+","+getAge());
}
}
public class Teacher extends People{
public void show(){
System.out.println("老师的信息"+getName()+","+getAge());
}
}
public class Test {
public static void main(String[] args) {
Student s = new Student();
s.setName("zs");
s.setAge(18);
Teacher t = new Teacher();
t.setName("ww");
t.setAge(39);
register(s);
register(t);
}
public static void register(People p){
p.show(); //学生的信息zs,18;老师的信息ww,39
}
}
多态中调用成员的特点
变量调用:编译看左边,运行也看左边
方法调用:编译看左边,运行看右边
堆内存有个方法区,test->animal->dog;new出来后堆内存一个地方有两个name,在子类的对象中,会把父类的成员变量也继承下去,子类中对方法进行了重写,在虚方法表汇总会把父类的方法覆盖
psvm{
Animal a = new Dog();
sout:a.name // animal
a.show(); // Dog
}
class Animal{name,show()}
class Dog extends Animal{anme,show()}
-
调用成员变量: 编译看左边,运行也看左边
-
编译看左边:javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败。
-
运行也看左边: java运行代码的时候,实际获取的就是左边父类中成员变量的值
-
-
调用成员方法: 编译看左边,运行看右边
- 编译看左边:javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有编译失败。
- 运行看右边: java运行代码的时候,实际上运行的是子类中的方法。
public class Animal {
String name = "动物";
public void show(){
System.out.println("animal");
}
}
class Dog extends Animal{
String name = "狗";
@Override
public void show(){
System.out.println("dog");
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Dog();
System.out.println(a.name);//动物
a.show(); // dog
}
}
多态的优势和弊端
-
优势
- 在多态形式下,右边的对象可以实现解耦合,便于扩展和维护
Person p = new Student(); p.work; // 业务逻辑发生改变,后续代码无需修复,比如换成老师工作,work里的方法会自动变
- 定义方法时,使用父类作为参数,可以接收所有子类对象,体现多态的扩展性与遍历
-
弊端:不能调用子类的特有方法
- 调用成员方法编译看左边运行看右边
-
引用数据类型转换
- 方式1:自动类型转换
Person p = new Student()
- 方式2:强制类型转换
Student s = (Student)p
- 作用
- 可以转换成真正的子类类型,从而调用子类独有功能
- 转换类型与真实对象类型不一致会报错
- 转换的时候用instanceof关键字进行判断
- 方式1:自动类型转换
-
弊端解决:调用者变成子类类型就可以
Dog d = (Dog) a // Animal
d.lockHome();
- 判断要转换的类型
if(a instanceof Dog){
Dog d = (Dog) a;
d.lookHome();
}else if(a instanceof Cat){
Cat c = (Cat) a;
c.catchMouse();
}else{
...
}
jdk14新特性:先判断是否为dog类型,是则强转,并赋变量名d,不是不强转,结果false
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
...
}
代码块
- 局部代码块:方法里面;用完之后立马回收;执行完a就从内存里消失;
提前结束变量的声明周期,a只能在局部代码块里才能使用
节约内存
psvm{
{
int a = 1;
sout:a // 1
} //a从内存消失
sout:a // 无
}
- 构造代码块:写在成员位置的代码块;
优先于构造方法执行,创建本类对象时会先执行构造代码块再执行构造方法;
优先于构造方法,把多个构造方法中的重复的代码写在构造代码块【不够灵活】,有的不想用也没办法;
public class Student{
private String name;
private int age;
{
sout:开始创建对象了
}
public Student(){
//sout:开始创建对象了
}
public Student(String name,int age){
//sout:开始创建对象了;
this.name = name;
this.age = age;
}
}
多个构造方法有重复可以:把重复代码写在一个中,其他的构造方法里迪this调用本类其他构造
public class Student{
private String name;
private int age;
public Student(){
this(null,0);
}
public Student( String name,int age){
sout:开始创建对象了;
this.name = name;
this.age = age;
}
}
重复代码抽取成一个方法
public class Student{
private String name;
private int age;
public Student(){
方法()
}
public Student(String name,int age){
方法();
this.name = name;
this.age = age;
}
}
- 静态代码块:static{}
随着类的加载而加载,自动触发,只执行一次;在类加载的时候,做一些数据初始化的时候用
可以进行数据初始化,数据比较安全,可以在登录前先初始化一些用户信息
接口
不是所有子类都有某个行为
animal- -> 兔子(不会游泳),青蛙(会)、狗(会)
定义一个游泳的接口
就是一种规则,是对行为的抽象
public interface 接口名{}
接口不能实例化
接口和类之间是实现关系,通过implements关键字表示,可以多实现
public class 类名 implements 接口名{}
接口的子类称为实现类,要么重写接口中的所有抽象方法,要么实现类本身是抽象类
public class 类名 implements 接口名1,接口名2{}
public class 类名 extends 父类 implements 接口名1,接口名2{}
例子:
public interface Swim{
public abstract void swim();
}
抽象类更多的用在父类中,抽取共性方法的时候,方法体不一样就可以写成抽象方法,抽象方法所在的类就是抽象类
比如动物抽象类,接口是行为,比如游泳行为
接口中成员的特点
成员变量只能是常量
默认修饰符public static final
没有构造方法
成员方法只能是抽象方法,默认修饰符public abstract
【方法会默认自动加上public abstract】
jdk7以前接口中只能定义抽象方法
jdk8接口中可以定义有方法体的方法
jdk9接口中可以定义私有方法
接口与类之间的关系
多个接口中有重名方法,只需要重写一次
- 类和类的关系
- 继承关系,只能单继承,不能多继承,但是可以多层继承
- 类和接口的关系
- 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
- 接口和接口的关系
- 继承关系,可以单继承,也可以多继承
实现类实现了最下面的子接口需要重写所有的抽象方法
接口中新增的方法
- JDK7以前: 接口中只能定义抽象方法、
- JDK8的新特性: 接口中可以定义有方法体的方法。(默认、静态)
- JDK9的新特性: 接口中可以定义私有方法(普通的和静态的)
接口发生变化所有实现类就必须跟着改变,接口中的抽象方法不能拥有方法体;如果接口里面的方法可以有方法体就不用跟着修改了
public interface Inter{
public abstract int method();
//定义有方法体的方法
}
接口升级时:此时实现类就不需要立马修改了,等以后用到某个规则了,再重写就行了
public interface Inter{}
public class InterImplA implements Inter{}
public class InterImplB implements Inter{}
。。。
加入10个新方法
JDK8以后接口中新增的方法:
-
接口中默认方法
- 允许在接口中定义默认方法需要使用关键字default 修饰
- 作用: 解决接口升级的问题
- 接口中默认方法的定义格式:
- 格式:public default 返回值类型 方法名(参数列表){}
- 范例: public default void show(){}
- 接口中默认方法的注意事项
- 默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
- public可以省略,default不能省略
- 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
- 允许在接口中定义默认方法需要使用关键字default 修饰
-
接口中静态方法
- 允许在接口中定义定义静态方法,需要用static修饰
- 接口中静态方法的定义格式
- 格式:public static 返回值类型 方法名(参数列表){}
- 范例: public static void show()[
- 接口中静态方法的注意事项
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,static不能省略
public interface Inter {
public abstract void method();
public static void show() {
System.out.println("接口中的静态方法");
}
}
public class interImpl implements Inter{
@Override
public void method() {
System.out.println("只要求重写抽象方法,静态方法不需要重写");
}
//不叫重写,只是一个重名方法o.0
public static void show(){
System.out.println("重写");
}
}
public class Test {
public static void main(String[] args) {
Inter.show();
interImpl.show();
// 子类把从父类继承下来的虚方法里面的方法进行覆盖了才叫重写
// 静态的,私有的,最终的不会添加到虚方法表
}
}
jdk9以后添加的私有方法
interface Inter{
public default void m1(){
sout;
log();
}
public default void m2(){
sout;
log();
}
}
//public defult void log(){
//不想让别人用,没有意义,只是一些重复的代码
private void log(){
sout:方法
}
接口中私有方法的定义格式
-
private 返回值类型 方法名(参数列表){}
private void show(){}
-
private static 返回值类型 方法名(参数列表){}
private static void method(){}
public interface InterA {
public default void show1(){
System.out.println("show1");
show3();
}
public static void show2(){
System.out.println("show2");
show4();
}
//普通的私有方法,给默认方法服务的
private void show3(){
System.out.println("3");
}
//静态的私有方法,给静态方法服务的
private static void show4(){
System.out.println("4");
}
}
接口的应用
编译看左边运行看右边
接口代表规则,是行为的抽象,想要让哪个类拥有一个行为,就让这个类实现对应的接口就行
当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称为接口多态
public interface 运输{}
public void 搬家(运输接口 c){}
接口类型 j = new 实现类对象();
搬家(车对象);
搬家(搬家公司);
适配器设计模式
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。设计模式就是各种套路
适配器设计模式:解决接口与接口实现类之间的矛盾问题
接口中方法太多,有十几个;实现的时候要重写所有的,但是现在只要第5个
适配器设计模式:在中间新建一个类,让中间这个类实现接口,空实现所有方法,新类就不用实现借口了,只需要继承中间的适配器Adapter,新类中需要用到哪个方法就重写哪个方法
适配器是空实现,一般写成抽象的,不让外界创建适配器对象
当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以适配器设计模式
- 编写中间类XXXAdapter,实现对应的接口
- 对接口中的抽象方法进行空实现
- 让真正的实现类继承中间类,并重写需要用的方法
- 为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰
实现类还要继承其他的类的话,让适配器继承其他类就行
内部类
类的五大成员:属性,方法,构造方法,代码块,内部类
在一个类里面再定义一个类
public class Outer{ // 外部类
public class Inter{ // 内部类
}
}
public class Test{psvm} // 外部其他类
在外部其他类中创建内部类的对象并调用内部类的方法
- 应用场景
b类表示的事物是a事物的一部分,且b单独存在没有意义,比如汽车的发动机,arrayList的迭代器,人的心脏等
public class car{
String carName;
int carAge;
int carColor;
public void show(){
sout:this.carName;
Engine e = new Engine();
sout:e.engineName
}
class Engine{
String engineName;
int engineAge;
public void show(){
sout:carName;
sout:engineName
}
}
}
内部类表示的事物是外部类的一部分,内部类单独出现没有意义;
内部类可以直接访问外部类的成员,包括私有;
外部类要访问内部类的成员必须创建对象;
成员内部类
写在成员位置的,属于外部类的成员
成员内部类可以被一些修饰符所修饰;如private,默认,protected,public,static
$区分内部类
public class car{ // 外部类
String carName;
int carAge;
int carColor;
class Engine{ // 成员内部类
String engineName;
int engineAge;
}
}
-
获取
-
当成员内部类被private修饰时,在外部类编写方法,对外提供内部类对象
-
当成员内部类被非私有修饰时,直接创建对象【外部类名.内部类名 对象名 = 外部类对象. 内部类对象;】
Outer.Inner oi = new Outer().new Inner();
-
-
外部类成员变量月内部类成员变量重名时,内部类访问
Outer.this.变量名
-
private修饰的话
-
用piblic
-
外部类提供一个方法,方法中返回内部类的对象,在外部通过方法获取内部类的对象
-
package neibulei;
public class Outer {
String name;
public class Inner{
}
public Inner getInstance(){
return new Inner();
}
}
package neibulei;
public class Test {
public static void main(String[] args) {
Outer.Inner oi = new Outer().new Inner();
Outer o = new Outer();
System.out.println(o.getInstance());
//neibulei.Outer$Inner@4eec7777
}
}
在成员内部类里,jdk16之前不能定义静态变量
成员内部类如何获取外部类的变量
package neibulei;
public class Outer2 {
private int a = 10;
class Inner{
private int a = 20;
public void show(){
int a = 30;
System.out.println(a);
System.out.println(this.a);
//inner和outer不是继承关系,不能用super
System.out.println(Outer2.this.a);
//编译之后Outer和Inner是两个独立的文件
//Outer2$Inner.class
}
}
}
package neibulei;
public class Test2 {
public static void main(String[] args) {
Outer2.Inner oi = new Outer2().new Inner();
oi.show();
}
}
静态内部类
静态内部类是一种特殊的成员内部类
只能访问外部类中的静态变量和静态方法
访问非静态的需要创建对象,根据对象访问变量和方法
public class Car{
String carName;
int carAge;
static class Engine{
String engineName;
int engineAge;
}
}
- 静态内部类对象的方式:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
Outer.Inner oi = new Outer.Inner();
-
调用静态内部类中的方法:
-
非静态方法: 先创建对象,用对象调用
-
静态方法: 外部类名.内部类名.方法名()
-
package neibulei;
public class Outer {
int a = 10;
static int b = 20;
static class Inner{
public void show1(){
System.out.println("非静态方法show1");
// System.out.println(a);//报错
Outer o = new Outer();
System.out.println(o.a);
System.out.println(b);
}
public static void show2(){
System.out.println("静态方法show2");
// System.out.println(a);//报错
Outer o = new Outer();
System.out.println(o.a);
System.out.println(b);
}
}
}
package neibulei;
public class Test {
public static void main(String[] args) {
//静态的都可以类名.获取
Outer.Inner oi = new Outer.Inner();
oi.show1();
oi.show2();
Outer.Inner.show2();
}
}
局部内部类
将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量
外界是无法直接使用,需要在方法内部创建对象并使用。
该类可以直接访问外部类的成员,也可以访问方法内的局部变量
package neibulei;
public class Outer4 {
public void show(){
int a = 10;
int b = 20;
//局部内部类
class Inner{
String name;
int age;
public void p(){
System.out.println(a);
System.out.println(b);
}
public void method1(){
System.out.println("局部内部类里的1方法");
}
public static void method2(){
System.out.println("局部内部类里的静态2方法");
}
}
//创建局部内部类的对象
Inner i = new Inner();
System.out.println(i.name);
System.out.println(i.age);
i.method1();
Inner.method2();
}
package neibulei;
public class Test4 {
public static void main(String[] args) {
Outer4 o = new Outer4();
//无法直接调用show方法里的a
// o.a;
o.show();
//null
//0
//局部内部类里的1方法
//局部内部类里的静态2方法
}
}
匿名内部类
隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置
-
匿名内部类的格式
new 类名或者接口名(){ 重写方法; };
-
格式的细节
- 包含了继承或实现,方法重写,创建对象
- 整体就是一个类的子类对象或者接口的实现类对象
-
使用场景
- 当方法的参数是接口或者类时;以接口为例,可以传递这个接口的实现类对象;如果实现类只要使用一次,就可以用匿名内部类简化代码
把前面的class删掉,剩余的内容就变成了一个没有名字的类
这个没有名字的类想要实现Swim接口把Swim写在了大括号的前面,表示这个没有名字的类实现了Swim接口,所以需要在类中重写接口里面所有的抽象方法
还想要创建这个没有名字的类的对象,new
对应实现(继承)关系,方法的重写,创建对象
new的不是swim接口,而是后面的类
名字只是不需要自己起,是外部类$序号.class
public class Student implements Swim{
@Override
public void swim(){
System.out.println("重写")
}
}
public interface Swim{
public abstract void swim();
}
new Swim(){
@Override
public void swim(){
System.out.println("重写")
}
}
应用
测试类调用method方法
zi extends Animal{
Dog d = new Dog();
method(d)
}
如果dog类只用一次,单独定义一个子类太麻烦
method(
new Animal(){
@Override
public void eat(){
System.out.println("重写方法")
}
}
);
public static void method(Animal a){ //Animal a= 子类对象,多态
a.eat(); //编译看左边,
}
接口多态,swim接口的实现对象
Swim s = new Swim(){
@Override
public void swim(){
System.out.println("重写方法")
}
};
s.swim();
整体是个对象,后面的{}才是类
new Swim(){
@Override
public void swim(){
System.out.println("重写方法")
}
}.swim();
//对象可以调用自己的方法
标签:子类,void,基础,接口,public,面向对象,方法,class
From: https://www.cnblogs.com/lmcool/p/oop.html