九 面向对象编程(高级部分)
1.类变量
(1)概念:类变量(静态变量/静态方法),是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改他时,修改的也是同一个变量。
(2)定义语法:访问修饰符 static 数据类型 变量名;
或者是:static 访问修饰符 数据类型 变量名;
(3)如何访问类变量:类名.类变量名 或者 对象名.类变量名
(4)细节:类变量在类加载时就初始化了,即使没有创建对象,只要类加载了,就可以使用类变量。
2.类方法(静态方法)
(1)定义语法:访问修饰符 static 数据类型 方法名(){}
或者是:static 访问修饰符 数据类型 方法名(){}
(2)调用:类名.类方法名 或者 对象名.类方法名
(3)细节:类方法中不能使用和对象有关的关键字 比如this super;静态方法只能访问静态成员,非静态的方法,可以访问静态成员和非静态成员
3.main方法语法
(1)main方法形式:public static void main(String[] args)
4.代码块
(1)基本语法:[修饰符]{代码};
修饰符可写可不写,要写只能写static
代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的叫普通代码块。
(2)细节:
public class CodeBlockDetail01 {
public static void main(String[] args) {
//static代码块随着类的加载而执行,并且只会执行一次;如果是普通代码块,每创建一个对象,就执行
//类什么时候被加载
//1.创建对象实例(new)
AA aa = new AA();
//2.创建子类对象实例时,父类也会被加载
BB bb = new BB();
//3.使用类的静态成员时
System.out.println(cat.n1);
}
}
class AA{
static{
System.out.println("AA静态代码块被执行");
}
}
class BB extends AA{
static {
System.out.println("BB静态代码块被执行");
}
}
class cat extends Animal{
public static int n1 = 10;
static {
System.out.println("cat静态代码块被执行");
}
}
class Animal{
static {
System.out.println("Animal静态代码块被执行");
}
}
4).创建一个对象时,在一个类调用顺序是:
(1)调用静态代码块和静态属性初始化
(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)
(2)调用普通代码块和普通属性的初始化
(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
(3)调用构造方法。*/
/*
5).构造器 的最前面其实隐含了super()和 调用普通代码块,
静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于 构造器和普通代码块执行的
class A {
public A() {//构造器
//这里有隐藏的执行要求
//(1) super();
//(2) 调用普通代码块
System.out.println("ok");
6)我们看一下创建一个子类对象时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
父类的构造方法
子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
子类的构造方法
5.final关键字
(1)final 可以修饰类、属性、方法和局部变量.
在某些情况下,程序员可能有以下需求,就会使用到final:
1)当不希望类被继承时,可以用final修饰.
2) 当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。 访问修饰符 final 返回类型 方法名
3)当不希望类的的某个属性的值被修改,可以用final修饰。 public final double TAX RATE=0.08
4)当不希望某个局部变量被修改,可以使用final修饰。final double TAX RATE=0.08
(2)细节:
1)如果 final 修饰的属性是静态的,则初始化的位置只能是 1:定义时 2:在静态代码块 不能在构造器中赋值。
2)final 类不能继承,但是可以实例化对象
3)如果类不是 final 类,但是含有 final 方法,则该方法虽然不能重写,但是可以被继承 即,仍然遵守继承的机制
4)一般来说,如果一个类已经是 final 类了,就没有必要再将方法修饰成 final 方法
5)final 和 static 往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化处理
6)包装类(Integer,Double,Float,Boolean)String类也是final类
6.抽象类
(1)基本介绍:
1)用abstract 关键字来修饰一个类时,这个类就叫抽象类访问修饰符 abstract 类名{}
2)用abstract 关键字来修饰一个方法时,这个方法就是抽象方法访问修饰符 abstract 返回类型 方法名(参数列表);//没有方法体
3) 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类()
(2)细节:
public class AbstractDetail01 {
public static void main(String[] args) {
//A a = new A();//抽象类不能被实例化
}
}
//抽象方法不能使用private,final,static 修饰,这些关键字与重写违背
//抽象类不一定包含abstract方法
abstract class A{
public void hi(){
System.out.println("hi");
}
}
//一旦该类包含了abstract方法,这个类必须声明为abstract
abstract class B{
public abstract void hi();
}
class C{
//abstract只能修饰类和方法,不能修饰属性和其他的
//public abstract int ni=1;
}
抽象方法不能使用private final和static 来修饰,这些关键字与重写相违背
7.接口
(1)基本介绍:接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。
语法: interface 接口名{
//属性//抽象方法
}
class 类名 implements 接口{
自己属性;
自己方法;
必须实现的接口的抽象方法
}
(2)细节:
public class InterfaceDetail01 {
//new IA;错误
}
//1.接口不能被实例化
//2.接口中所有的方法是public 方法,接口中抽象方法,可以不用abstract来修饰
//3.一个普通类实现接口,就必须将接口的所有方法都实现,可以用alt+enter
//4.抽象类去实现接口,可以不实现接口的抽象方法
interface IA{
void say();
void hi();
}
class Cat implements IA{
@Override
public void say() {
}
@Override
public void hi() {
}
}
abstract class Tiger implements IA{
}
public class InterfaceDetail02 {
public static void main(String[] args) {
System.out.println(IB.n1);//证明是static的
//IB.n1 = 30;不能修改,是final的
}
}
interface IB{
//接口中的属性只能是final的,而且是public static final 来修饰
int n1 =10;//等价于public static final n1 =10;
void hi();
}
interface IC{
void say();
}
//一个接口不能继承其他的类,但是可以继承多个别的接口
//接口的修饰符是public 或者默认,和类的修饰符一样
interface IE extends IB,IC{
}
//一个类可以实现多个接口
class AA implements IB,IC{
@Override
public void hi() {
}
@Override
public void say() {
}
}
(3)实现接口vs继承类
当子类继承了父类,就自动拥有父类的功能 如果子类需要扩展功能,可以通过实现接口的方式扩展 实现接口是对Java单继承机制的补充
8.内部类
(1)如果定义类在局部位置(方法中/代码块) :1) 局部内部类(有类名) 2) 匿名内部类(没有类名)
定义在成员位置: 1) 成员内部类(没有static修饰) 2) 静态内部类(有static修饰)
(2)局部内部类
public class LocalInnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.m1();
System.out.println("outer02 的 hashcode=" + outer02);
}
}
class Outer02 {//外部类
private int n1 = 100;
private void m2() {
System.out.println("Outer02 m2()");
}//私有方法
public void m1() {//方法
//1.局部内部类是定义在外部类的局部位置,通常在方法
//3.不能添加访问修饰符,但是可以使用 final 修饰
//4.作用域 : 仅仅在定义它的方法或代码块中
final class Inner02 {//局部内部类(本质仍然是一个类)
//2.可以直接访问外部类的所有成员,包含私有的
private int n1 = 800;
public void f1() {
//5. 局部内部类可以直接访问外部类的成员,比如下面 外部类 n1 和 m2()
//7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,
// 使用 外部类名.this.成员)去访问
// Outer02.this 本质就是外部类的对象, 即哪个对象调用了 m1, Outer02.this 就是哪个对象
System.out.println("n1=" + n1 + " 外部类的 n1=" + Outer02.this.n1);
System.out.println("Outer02.this hashcode=" + Outer02.this);
m2();
}
}
//6. 外部类在方法中,可以创建 Inner02 对象,然后调用方法即可
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
匿名内部类
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f1();
//外部其他类---不能访问----->匿名内部类
System.out.println("main outer05 hashcode=" + outer05);
}
}
class Outer05 {
private int n1 = 99;
public void f1() {
//创建一个基于类的匿名内部类
//不能添加访问修饰符,因为它的地位就是一个局部变量
//作用域 : 仅仅在定义它的方法或代码块中
Person p = new Person(){
private int n1 = 88;
@Override
public void hi() {
//可以直接访问外部类的所有成员,包含私有的
//如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
System.out.println("匿名内部类重写了 hi 方法 n1=" + n1 +
" 外部内的 n1=" + Outer05.this.n1 );
//Outer05.this 就是调用 f1 的 对象
System.out.println("Outer05.this hashcode=" + Outer05.this);
}
};
p.hi();//动态绑定, 运行类型是 Outer05$1
//也可以直接调用, 匿名内部类本身也是返回对象
// class 匿名内部类 extends Person {}
// new Person(){
// @Override
// public void hi() {
// System.out.println("匿名内部类重写了 hi 方法,哈哈...");
// }
// @Override
// public void ok(String str) {
// super.ok(str);
// }
// }.ok("jack");
}
}
class Person {//类
public void hi() {
System.out.println("Person hi()");
}
public void ok(String str) {
System.out.println("Person ok() " + str);
}
}
匿名内部类最佳实践:当作实参直接传递,简洁高效 例:
public class InnerClassExercise01 {
public static void main(String[] args) {
//当作实参直接传递,简洁高效
f1(new IL() {
@Override
public void show() {
System.out.println("这是一副名画");
}
});
}
public static void f1(IL il){
il.show();
}
}
interface IL{
void show();
}
成员内部类
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.t1();
//外部其他类,使用成员内部类的三种方式
// 第一种方式
outer08.new Inner08(); //相当于把 new Inner08()当做是 outer08 成员
// 这就是一个语法,不要特别的纠结.
Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say();
// 第二方式 在外部类中,编写一个方法,可以返回 Inner08 对象
Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
inner08Instance.say();
}
}
class Outer08 { //外部类
private int n1 = 10;
public String name = "张三";
private void hi() {
System.out.println("hi()方法...");
}
//1.注意: 成员内部类,是定义在外部内的成员位置上
//2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
public class Inner08 {//成员内部类
private double sal = 99.8;
private int n1 = 66;
public void say() {
//可以直接访问外部类的所有成员,包含私有的
//如果成员内部类的成员和外部类的成员重名,会遵守就近原则. //,可以通过 外部类名.this.属性 来访问外部类的成员
System.out.println("n1 = " + n1 + " name = " + name + " 外部类的 n1=" + Outer08.this.n1);
hi();
}
}
//方法,返回一个 Inner08 实例
public Inner08 getInner08Instance(){
return new Inner08();
}
//写方法
public void t1() {
//使用成员内部类
//创建成员内部类的对象,然后使用相关的方法
Inner08 inner08 = new Inner08();
inner08.say();
System.out.println(inner08.sal);
}
}
静态内部类
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.m1();
//外部其他类 使用静态内部类
//方式 1
//因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
//方式 2
//编写一个方法,可以返回静态内部类的对象实例.
Outer10.Inner10 inner101 = outer10.getInner10();
System.out.println("============");
inner101.say();
Outer10.Inner10 inner10_ = Outer10.getInner10_();
System.out.println("************");
inner10_.say();
}
}
class Outer10 { //外部类
private int n1 = 10;
private static String name = "张三";
private static void cry() {}
//Inner10 就是静态内部类
//1. 放在外部类的成员位置
//2. 使用 static 修饰
//3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
//4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
//5. 作用域 :同其他的成员,为整个类体
static class Inner10 {
private static String name = "韩顺平教育";
public void say() {
//如果外部类和静态内部类的成员重名时,静态内部类访问的时,
//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)
System.out.println(name + " 外部类 name= " + Outer10.name);
cry();
}
}
public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问
Inner10 inner10 = new Inner10();
inner10.say();
}
public Inner10 getInner10() {
return new Inner10();
}
public static Inner10 getInner10_() {
return new Inner10();
}
}