深入类和对象
Posted Al_tair
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入类和对象相关的知识,希望对你有一定的参考价值。
深入类和对象
大家好呀!我是小笙!我学习了韩顺平老师的类和对象的知识,收获颇丰!现在来和大家分享笔记!
深入类和对象
类变量和类方法
类变量也称作静态变量,类方法也称作静态方法
与成员变量的区别就是有无 static 关键字
注意细节
- 静态变量是同一个类所有对象共享
- 类变量在类加载的时候就生成了
- 类变量中不能使用和对象有关的关键字(比如:,this,super ),因为this或者super的产生需要创建对象,但是类变量在类加载的时候就出现了
- 类方法只能访问类变量或者类方法,但是反之普通成员方法既可以访问非静态成员,也可以访问静态成员
- 类变量的生命周期随着类的加载开始,随着类消亡而销毁
- 如果父类中含有一个静态方法,且在子类中也含有一个返回类型、方法名、参数列表均与之相同的静态方法,那么该子类实际上只是将父类中的该同名方法进行了隐藏,而非重写。换句话说,父类和子类中含有的其实是两个没有关系的方法,它们的行为也并不具有多态性
语法:访问修饰符 static 数据类型 变量名; (访问修饰符和static的顺序可以交换)
访问方式: 类名.类变量(推荐) 对象名.类变量名
// 举例类变量和类方法的使用
// Math类的源码
public static final double PI = 3.14159265358979323846;
// 取绝对值
@HotSpotIntrinsicCandidate
public static int abs(int a)
return (a < 0) ? -a : a;
Main方法
解释main方法的形式; pubLic static void main(String[]args)
public class HelloMain
/*
* 1.main方法时虚拟机调用
* 2.java虚拟机调用类的main方法,所以该方法的访问权限必须是public
* 3.java虚拟机在执行main()方法时不必创建对象,所以用static
* 4.Java执行程序 参数1 参数2 参数3【举例说明: java HelloMain hello main !】 参数1:hello 参数2:main 参数3:!
*
* 特别提示:
* 1.在main()方法中,我们可以直接调用main方法所在的类的静态方法和静态属性
* 2.但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象访问非静态成员
*/
public static void main(String[]args)
// args 是如何传入 在idea上设置 Run -> Edit Configurations -> program arguments:
// 遍历显示
for(int i=0;i<args.length;i++)
System.out.println("第"+(i+1)+"个参数"+args[i]);
代码块
普通代码块
普通代码块又称初始化块,属于类中的成员部分,没有方法名,没有返回,没有形参,只有方法体
普通代码块不需要被调用,而是加载对象的时候隐式调用
语法: // 方法体 ; 没有修饰符 ;分号可有可无
public class CodeBlock
/*
* 代码块
* 1.相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
* 2.场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性
*/
public static void main(String[]args)
new Movie("爱情神话");
class Movie
private String name;
private double price;
private String director;
/*
* 下面的三个构造函数都有相同的语句,这样看起来重复
* 代码块的使用就可以很好的解决这个问题
*/
System.out.println("电影开始~~");
;
public Movie()
public Movie(String name)
// System.out.println("电影开始~~");
this.name = name;
public Movie(String name,double price)
// System.out.println("电影开始~~");
this.name = name;
this.price = price;
public Movie(String name,double price,String director)
// System.out.println("电影开始~~");
this.name = name;
this.price = price;
this.director = director;
注意细节
- 普通代码块是随着对象的创建而执行的,有几个对象就运行几次代码块
- 如果不创建对象,只是使用类的静态成员,不会执行代码块
静态代码块
静态代码块,属于类中的成员部分,没有方法名,没有返回,没有形参,只有方法体和修饰符static
静态代码块不需要被调用,而是加载类的时候隐式调用
语法:static // 方法体 ; ;分号可有可无
类什么时候被加载?【重点】
- 创建对象化实例时,并且父类的也会被加载
- 使用类的静态成员时(静态属性,静态方法)
- 使用子类的静态成员时
静态代码块的例子
public class StaticCodeBlock
/*
* 父类和子类的静态代码块和普通代码块执行顺序是什么呢?
* 在父类和子类都有静态代码块和普通代码块时候,创建子类实例时候的执行顺序: 父类静态代码块 --> 子类静态代码块 --> 父类普通代码块 --> 子类普通代码块
* 在父类和子类都有静态代码块和普通代码块时候,创建父类实例时候的执行顺序: 父类静态代码块 --> 父类普通代码块
* 在父类和子类都有静态代码块和普通代码块时候,使用父类的静态成员时只调用父类的静态代码块
* 在父类和子类都有静态代码块和普通代码块时候,使用子类的静态成员时候的执行顺+序: 父类静态代码块 --> 父类普通代码块
*
* 静态属性初始化,静态代码块,静态方法,构造器的优先级
* 1.父类的静态属性初始化优先级 == 静态代码块优先级 (看代码的执行顺序)
* 2.子类的静态属性初始化优先级 == 静态代码块优先级 (看代码的执行顺序)
* 3.父类的成员变量初始化优先级 == 普通代码块优先级 (看代码的执行顺序)
* 4.父类的构造器
* 5.子类的成员变量初始化优先级 == 普通代码块优先级 (看代码的执行顺序)
* 6.子类的构造器
* 方法都需要调用才会执行
* public 构造函数名()
* 1)super()
* 2)普通代码块
* 3)构造函数内容
*
*/
public static void main(String[]args)
// 使用类的静态成员时(静态属性,静态方法)
// AA.name = "念笙";
// AA.show();
// 创建对象实例时(new)
// new AA();
// 创建子类的对象实例,父类也会被加载
// new BB();
// 使用子类的静态成员时(静态属性,静态方法)
// BB.sex = "男";
// BB.show();
// 静态初始化,静态代码块,静态方法的优先级
new CC();
// CC.show();
class AA
public static String name;
static
System.out.println("AA静态代码块被调用");
System.out.println("AA普通代码块被调用");
public static void show()
System.out.println("name:"+name);
class BB extends AA
public static String sex;
static
System.out.println("BB静态代码块被调用");
System.out.println("BB普通代码块被调用");
public static void show()
System.out.println("sex:"+sex);
class CC
public CC()
// 1)super()
// 2)普通代码块
System.out.println("构造器被调用");
// 静态属性初始化
private static int age = getAge();
// 静态方法
public static void show()
System.out.println("age:"+age);
// 静态代码块
static
System.out.println("CC静态代码块被调用");
public static int getAge()
System.out.println("age:"+age);
return 18;
final关键字
final 关键字可以用来修饰类,属性,方法和局部变量 不能修饰构造器
public class Final
/*
* 使用final的情况
* (1)当不希望类被继承时,可以用final修饰
* (2)当不希望父类的某个方法被子类覆盖或者重写
* (3)当不希望类的某个属性的值被修改,可以用final修饰 [例如:final double PI = 3.1415926] -> 常量
* (4)当不希望某个局部变量被修改,可以用final修饰 -> 局部常量
*
* 使用final关键字的细节讨论
* 1.final修饰的属性又叫常量,一般用 XXX_XXX_XXX来命名
* 2.final修饰的属性在定义时,必须赋初始值,赋值可以加在如下的位置上
* 属性定义时初始化 / 在构造器中 / 在代码块中
* final修饰的属性是静态的,则,赋值可以加在如下的位置上
* 静态属性定义时初始化 / 在静态代码块中
* 3.final类不能继承,但是可以实例化对象
* 4.如果类不是final类,但是含有final方法,虽然该方法不能重写,但是该类可以被继承
* 5.final 和 static 往往搭配使用效率更高,不会导致类加载,底层编译器做了优化 如下例子
* 6.包装类(Integer,double,Float,Boolean等)不能继承
*/
public static void main(String[] args)
/*
* final 修饰基本数据类型
* 基本数据类型的值不能发生变化
*/
final int age = 30;
// sge = 100; 报错
/*
* final修饰引用数据类型
* 引用数据类型饿地址值不能发生改变,但是该地址上的内容可以发生改变
*/
final FU fu = new FU();
// fu.show4();
// fu = new FU(); 报错
// final 和 static 往往搭配使用效率更高,不会导致类加载,底层编译器做了优化
System.out.println(BB.name);
class AA
// 静态属性定义时初始化
public static final int NUMS1 = 12;
public final int NUM1 = 12;
public static final int NUMS2;
public final int NUM2;
public static final int NUMS3;
public final int NUM3;
// 在静态代码块中初始化
static
NUMS2 = 12;
NUMS3 = 12;
NUM3 = 12;
// 在构造器中初始化
public AA()
NUM2 = 12;
// final 和 static 往往搭配使用效率更高,不会导致类加载,底层编译器做了优化
class BB
public static final String name = "yyds";
static
System.out.println("类加载了");
// 运行效果:不会显示类加载了 说明类没有被加载
抽象类
引出:当父类的某些方法需要申明,但又不确定如何实现的时候,可以把这个类变成抽象类 添加 abstract关键字
抽象类的本质还是类可以有方法,属性,代码块,构造器,但是抽象类不能被实例化
语法:
- 类:访问修饰符 abstract class 类名
- 方法:访问修饰符 abstract 返回类型 方法名(参数列表); // 没有方法体
public class AbstractDemo
/*
* 抽象类 ==> 简化父类方法的不确定代码
* 抽象类会被继承,由自己的子类实现方法
*
* 注意事项:
* 1.抽象类不能被实例化,也不能直接通过类名.静态属性的方法来访问抽象类中的静态属性,可以被子类用super关键字调用构造器
* 2.抽象方法对应抽象类或者接口;但是有抽象类,不仅可以没有抽象方法还可以有方法体的普通方法
* 3.abstract 只能修饰类和方法
* 4.如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract,但是如果子类实现抽象类的全部抽象方法,则子类的子类就不需要 * 实现了抽象方法
* 5.抽象方法不能使用private,final和static来修饰,因为这些关键字都是和重写相违背的
*/
public static void main(String[] args)
// 抽象类不能被实例化
// Animal animal = new Animal(); 报错
// 当一个类中存在抽象方法时候,需要将该类声明成为abstract类
abstract class Animal
public String name;
// 思考:有意义吗?? ==> 方法不确定性 ==> 考虑设计成抽象类方法(abstract)即是没有方法体 ==> 让子类继承实现
// public void eat()
// System.out.println("吃撒");
//
public abstract void eat();
接口
概念:接口在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
语法:访问修饰符 interface 接口名 访问修饰符:public 和默认 ; class 类名 implements 接口 // 必须实现的抽象方法
注意细节
-
在jdk7之前都是没有方法体的抽象方法,在jdk8以后接口的方法可以有具体实现,但是必须是静态方法或者是默认方法
// 接口中的所有方法都是public修饰的方法,接口中的抽象方法可以不用被abstract修饰 // 可以通过接口名.静态方法名来调用静态方法 接口名.属性名来访问属性 interface Interface // 属性 默认是public static final修饰 int n1 = 10; // 抽象方法 默认是public abstract void method1(); // 默认方法 添加default关键字 默认是public default public void method2() System.out.println("默认方法"); // 静态方法 public static void method3() System.out.println("静态方法");
-
接口不能被实例化
-
一个非抽象类实现了该接口就必须实现该接口上的所有抽象方法,抽象类去实现接口时候,可以不用实现接口的抽象方法
-
一个类同时可以实现多个接口,一个接口不能继承其他的类,但是可以继承多个其他接口
// 顶级接口 interface top // 父级接口 一个接口不能继承其他的类,但是可以继承多个其他接口 interface secondTop extends top //另外一个接口 interface another // 实现类 一个类同时可以实现多个接口 class imp implements secondTop,another
实现接口 Vs 继承类
-
接口和继承解决的问题不同
继承的价值主要在于:解决代码的复用性和可维护性
接口的价值主要在于:设计好各种规范(方法),让其他类去实现这些方法 -
接口比继承更加灵活
继承满足is-a的关系,而接口只需满足like-a的关系 -
接口在一定程度上实现代码解耦[接口的规范性+动态绑定]
抽象类和接口的区别
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
- 抽象可以有普通方法(含有方法体),接口只能是默认方法(带有default关键字)
接口的多态性
// Usb接口
public interface UsbInterface
// 规定相关接口的方法
public void start();
public void end();
// 相机实现了该接口
public class Camera implements UsbInterface
@Override
public void start()
System.out.println("照相机开始运行了~");
@Override
public void end()
System.out.println("照相机开始关机了~");
// 手机实现了该接口
public class Phone implements UsbInterface // 实现接口
@Override
public void start()
System.out.println("手机开始运行了~");
@Override
public void end()
System.out.println("手机开始关机了~");
// 接口插入,分别实现不同功能
public class Computer
// 计算机工作
public void work(UsbInterface usbInterface)
usbInterface.start();
usbInterface.end();
public class Interface01
/*
* 接口多态特性
* 1)既可以接收手机对象,又可以接受相机对象,就体现了接口多态(接口引用可以指向实现了该接口的类的对象)
* 2)多态数组
* usbs[i] instanceof Phone 向下转型,判读Usb接口是否为Phone
* 接口类型的变量可以指向实现了该接口的类的对象的实例
*/
public static void main(String[]args)
// 创建手机和相机对象
Camera camera = new Camera();
Phone phone = new Phone();
// 创建电脑对象
Computer computer = new Computer();
// 插入接口,相机和手机分别运作
computer.work(phone);
computer.work(camera);
// 多态数组
UsbInterface[] UsbS = new UsbInterface[2];
UsbS[0] = new Phone();
UsbS[1] = new Camera();
内部类
概念:一个类的内部嵌套了另一个类结构,被嵌套的类称为内部类,嵌套其他类的类称为外部类(注意类的五大成员:属性,方法,构造器,代码块,内部类)
class OuterClass // 外部类
int n1 = 100; // 属性
public OuterClass(int n3) // 构造方法
void n2() //方法
// 代码块
class InnerClass // 内部类
语法:class Outer // 外部类 class Inner // 内部类
内部类的分类:
- 定义在外部类局部位置(比如方法内)
- 局部内部类(有类名)
- 匿名内部类(没有类名,重点)
- 定义在外部类的成员位置上
- 成员内部类(没有static修饰)
- 静态内部类(有static修饰)
局部内部类
局部内部类是定义在外部类的局部位置(比如:方法中),并且有类名
作用域:仅在定义它的方法或代码块中
class OuterClass02 // 外部类
private int n1 = 100;
public void n2()
/*
* 局部内部类
* 1.局部内部类是定义在外部类的局部位置,通常在方法里或者代码块里
* 2.局部内部类可以访问外部类的所有成员变量,包含私有的
* 3.局部内部类不能添加访问修饰符,但是是可以被final修饰,可以不被继承
* 4.作用域:仅在定义它的方法或代码块中
* 5.外部类在方法中可以创建InnerPartClass对象,然后调用局部内部类的方法,不允许在方法外创建该对象
* 6.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果就想访问外部类的成员,则可以使用(外部类名.this.成员)
*/
class InnerPartClass
private int n1 = 20;
public void n3()
// 7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)
System.out.println(n1); // 20
// 解读OuterClass02.this 本质就是外部类的对象即那个对象调用了n2方法,OuterClass02.this就是那个对象,这里是out对象调用的,因此 OuterClass02.this指向out对象
System.out.println(OuterClass02.this.n1); // 100
System.out.println(OuterClass02.this); // com.Al_tair.innerClass_.OuterClass02@27d6c5e0
// 5.外部类在方法中可以创建InnerPartClass对象,然后调用局部内部类的方法
InnerPartClass innerPartClass = new InnerPartClass();
innerPartClass.n3();
public class InnerClass01 // 外部其他类
public static void main(String[] args)
OuterClass02 out = new OuterClass02();
out.n2();
System.out.println(out); // com.Al_tair.innerClass_.OuterClass02@27d6c5e0
匿名内部类
概念:定义在外部类的局部位置,没有类名
作用域:仅仅在定义它的方法和代码块中
语法:new 类名或接口名(参数列表) // 匿名内部类
public class AnonymousInnerClass
/*
* 匿名内部类
* 定义在外部类的局部位置<=>局部内部类(没有表露出来的类名)
* 基本语法:new 类或接口(参数列表)
*/
public static void main(String[] args)
OutClass outClass = new OutClass();
outClass.meathod();
class OutClass
private int n = 10;
public void meathod()
/*
* 使用匿名内部类简化开发
* aa的编译类型 -- AA ; aa的运行类型 -- 匿名内部类OutClass$1 (外部类名$匿名内部类的序号)
* 1.创建匿名内部类后马上创建该实例,并返回该地址给aa
* 2.匿名内部类只能使用一次,但是该实例对象可以反复引用,就是不能用一个匿名内部类创建多个对象实例
* 3.不能添加访问修饰符,因为它的就是一个局部变量
* 4.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)
*/
// 第一种调用匿名内部类的内部方法
// 这里new的不是接口实例化,而是接口的匿名内部实现类
AA aa = new AA() // 向上转型
@Override
public void cry()
System.out.println("嘤嘤嘤~~");
;
aa以上是关于深入类和对象的主要内容,如果未能解决你的问题,请参考以下文章