Java面向对象编程(高级部分)

类变量(静态变量)

在这里插入图片描述
在这里插入图片描述

class Child{
private String name;
//当我们给一个属性,增加了一个static修饰符
//该变量为所有的Child类的对象共享
public static int count = 0;
//定义一个变量count,是一个类变量(静态变量)static 静态
//该变量最大的特点就是会比Child类的所有的对象实例共享
//这个count不论在堆还是在方法区他都是可以共享的
}
什么是静态变量?

类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。

如何定义类变量?

定义语法:
访问修饰符 static 数据类型 变量名;
static 访问修饰符 数据类型 变量名

如何访问类变量?

类名.类变量名(规范)
对象名.类变量名

类变量使用注意事项和细节讨论

什么时候需要用类变量?
当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):比如:定义学生类,统计所有学生共交多少钱。Student (name,fee)

类变量与实例变量(普通属性)区别
类变量是该类的所有对象共享的,而实例变量是每个对象独享

加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量

类变量可以通过 类名.类变量名 或者对象名.类变量名 来访问,但java设计者推荐我们使用 类名.对象名方式访问 【前提是 满足访问修饰符的访问权限和范围】

类方法基本介绍

类方法又叫做静态方法
访问修饰符 static 数据返回类型 方法名() { }
static 访问修饰符 数据返回类型 方法名() { }

使用方式:
类名.类方法名
对象名.类方法名

类方法应用案例

public class StaticMethod
{
	public static void main(String[] args)
	{
		//创建2个学生对象,交学费
		Student tom = new Student("tom");
		tom.payFee(100);
		
		Student mary = new Student("mary");
		mary.payFee(200);
		//输出当前收到的总学费
		Student.showFee();//300
		
	}
}
class Student{
	public String name;
	//静态变量
	public static double totalFee = 0.0 ;
	public Student (String name,double fee)
	{
		this.name = name;
		totalFee += fee;
	} 
	//说明
	//1.当方法使用了static修饰后,该方法就是静态方法
	//2.静态方法就可以访问静态属性/变量
	public static double getTotalFee(){
	return totalFee;
	}
	public static void payFee(double fee)
	{
		Student.totalFee+=fee;
	}
	public static void showFee()
	{
		System.out.println("总学费有"+Student.totalFee);
	}
}

类方法的经典使用场景:
当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率

在程序员实际开发过程中,往往会将一些通用的方法,设计成静态方法,这样我们不需要创建对象就可以使用了,比如打印一维数组,冒泡排序。。。
eg:

//开发一个计算器类
class cal{
	public static double sum(double n1,double n2)
	{
		return n1+n2;
	}
}

==类方法中不允许使用和对象有关的关键字,比如this和super。==普通方法(成员方法)可以。
类方法(静态方法)中 只能访问静态变量或静态方法。
普通成员方法,既可以访问普通变量(方法),也可以访问静态变量(方法)。

小结:静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员

理解main方法语法

解释main方法的形式:public static void main(String[] args){}

  1. java虚拟即需要调用类的main()方法,所以该方法的访问权限必须是public
  2. java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static
  3. 该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数
  4. java运行的程序 参数1 参数2 参数3
    在这里插入图片描述
    在这里插入图片描述
    理解main方法语句
  5. 在main()方法中,我们可以直接调用main方法所在类的静态方法或静态属性
  6. 不能直接访问该类中的非静态成员,必须创建一个该类的实例对象后,才能同各国这个对象去访问类中的非静态成员。

代码块

代码块又称为初始化块,属于类中的成员【即 是类的一部分】,类似与方法,将逻辑语句封装在方法体中,通过{}包围起来。

但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。

基本语法:
[修饰符]{
代码
};
注意:

  1. 修饰符可选,要写的化也只能写static
  2. 代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的,叫普通代码块。
  3. 逻辑语句可以为任何逻辑语句(输入,输出,方法调用,循环,判断等)
  4. ;号可以写上,也可以省略
public class CodeBlock01{
}
class Movie{
	private String name;
	private double price;
	private String director;
	//如果每一次构造时都要讲一段固定的开场白
	//那么代码就会看起来很冗余
	//这时我们可以把相同的语句,放入到一个代码块中
	//我们不管调用哪个构造器来创建对象,都会先调用代码块然后再调用构造器 
	{
		System.out.println("电影就要开始了")
	}
	//构造器 => 重载
	public Movie(String name){
		this.name = name;
	}
	public Movie(String name,double price){
		this.name = name;
		this.price = price;
	}
	public Movie(String name,double price, String director){
		this.name = name;
		this.price = price;
		this.director = director;
	}
}
  1. static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象就执行。
  2. 类什么时候被加载?
    创建对象实例时(new)
    创建子类对象实例,父类也会被加载
    使用类的静态成员时(静态属性,静态方法)
  3. 普通的代码块,在创建对象实例时,会被隐式的调用
    被创建一次,就会调用一次。
    如果只是使用类的静态成员时,普通代码块并不会执行。
  4. 创建一个对象时,在一个类 调用的顺序是:(重点,难点):
    ①调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一 样,如果有多个静态代码块和多个静态变量)
    ②调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
    ③调用构造方法 构造器的最前面其实隐含了super()和调用普通代码块;而静态相关的代码块、属性初始化,在类加载时,就执行完毕,因此是优先于 构造器和普通代码块执行的
    class A
    {
    public A(){
    super();
    //调用普通代码块
    System.out.println(“ok”);
    }
    }
  5. 看一下创建一个子类时(继承关系),他们的:静态代码块、静态属性初始化;普通代码块、普通属性初始化;构造方法的调用顺序如下:
    ①父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
    ② 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
    ③父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
    ④父类的构造方法
    ⑤子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
    ⑥子类的构造方法
  6. 静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员

单例设计模式

什么是单例模式?
单例(单个的实例)
1.所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
2.单例模式有两种方式:1.饿汉式 2.懒汉式

单例模式应用实例:
步骤如下:
1.构造器私有化(防止对象可以直接去new来初始化)
2.类的内部创建对象
3.向外暴露一个静态的公共方法

//饿汉式   (无论有没有被使用,比如只使用该类中的其他静态变量并不需要创建实例就可以调用,可该类的实例也都已经创建了)
class SingleTon01{
	//将构造器私有化,防止直接new
	private SingleTon01(){};

	//我们提供一个静态属性类型就是SingleTon01
	//先创建号这个SingleTon01对象实例
	private static SingleTon01 instance = new SingleTon01();//instance是实例的意思

	//提供一个public的静态方法,可以返回instance
	public static SingleTon01 getInstance(){
		return instance;
	}
}
//懒汉式    (只有使用 才会创建实例)
public class SingleTon02{
	
}
//希望在程序中只能创建一个cat对象
//使用单例模式
public Cat{
	private String name;
	private static Cat cat;//只声明,不new创建
	//步骤
	//1.仍然构造器私有化
	//2.定义一个static静态属性对象
	//3.提供一个public的static方法,可以返回一个Cat对象
	private Cat(String name)
	{
		this.name = name;
	}
	public static Cat getInstance(){
		if(cat == null){
		//如果还没有创建cat对象
		cat = new Cat("小白猫")
		}
		return cat;
	}
}

饿汉式VS懒汉式
1.二者最主要的区别在于创建对象的时机不同;饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。
2.饿汉式不存在线程安全的问题,懒汉式存在线程安全问题。(可能多个线程同时getInstance,三个线程会创建三个对象,虽后面会保留最后创建的对象,但仍然纯真线程风险;学习线程后会多加完善)
3.饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题。
4.在我们javaSE标准类中,java.lang.Runtime就是经典的单例模式,饿汉式。

final关键字

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)

  5. final修饰的属性又叫常量,一般用 XX_XX_XX来命名(因为该属性的值不可被更改了)

  6. final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一【选择一个位置赋初值即可】:
    1. 定义时:如 public final double TAX_RATE=0.08
    2. 在构造器中
    3. 在代码块中
    在这里插入图片描述

  7. 如果final修饰的属性是静态的,则初始化的位置只能是:

    1. 定义时
    2. 在静态代码块 不能在构造器中赋值
  8. final类不能继承,但是可以实例化对象。

  9. 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。

  10. 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。

  11. final不能修饰构造方法(即构造器)

  12. final 和 static 往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理。

  13. 包装类(Integer,Double,Float,Boolean等都是final),String也是final类

抽象类

父类方法的不确定性:当父类的某些方法需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类
所谓抽象方法就是没有实现的方法。
所谓没有实现就是指,没有方法体
当一个类中存在抽象方法时,需要将该类声明为abstract类
一般来说,抽象类会被继承,由其子类来实现抽象方法

抽象类的介绍:

  1. 用abstract 关键字来修饰一个类时,这个类就叫抽象类 访问修饰符 abstract 类名{}
  2. 用abstract关键字来修饰一个方法时,这个方法就是抽象方法 访问修饰符 abstract 返回类型 方法名(参数列表);//没有方法体
  3. 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类
  4. 抽象类,是考官非常容易提问的知识点,在框架和设计模式使用较多
  5. 抽象类不能被实例化
  6. 抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
  7. 一旦类包含了abstract方法,则这个类必须声明为abstract。
  8. abstract只能修饰类和方法,不能修饰属性和其他的。
  9. 抽象类可以有任意成员【因为抽象类还是类】,比如:非抽象方法,构造器,静态属性等等
  10. 抽象方法不能有主体,也就是说,不可以实现
  11. 如果一个类继承了抽象类,他必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类
  12. 抽象方法不能使用private,final,和static来修饰,因为这些关键字都是和重写相违背的。

接口

接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来:
语法:

interface 接口名{
//1.写属性
//2.写方法(抽象的,默认实现方法,静态方法)
	//写属性
	public int n1=10;
	//写方法(在接口中,抽象方法,可以省略abstract关键字)
	public void hi();
	//在jdk8后,可以有默认实现方法,需要使用default关键字修饰
	default public void ok(){
		System.out.println("ok ...");
	}
	//jdk8后,可以有静态方法
	public static void cry(){
		System.out.println("cry ....");
	}
//在jdk7前,接口里的所有方法都没有方法体,即是抽象方法
//jdk8后接口可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现
}
class 类名 implements 接口{
	自己属性;
	自己方法;
	必须实现的接口的抽象类
}
  1. 接口不能被实例化
  2. 接口中所有的方法是public方法,接口中抽象方法,可以不用abstract修饰
  3. 一个普通类实现接口,就必须将该接口的所有方法都实现
  4. 抽象类实现接口,可以不用实现接口的方法。
  5. 一个类可以实现多个接口
  6. 接口中的属性,只能是final的,而且是public static final 修饰符。
    比如:int a=1;实际上是public static final int a = 1;(必须初始化)
  7. 接口中属性的访问形式:接口名.属性名
  8. 一个接口不能继承其他类,但是可以继承多个别的接口
  9. 接口的修饰符 只能是public 和 默认 这点和类的修饰符是一样的

接口的多态特性
多态传递特性:

/*
演示多态传递现象
*/
public calss InterfacePolyPass{
	public static void main(String[] args){
		//接口类型的变量可以指向实现该接口的对象实例
		IG ig = new Teacher();
		//如果IG继承了IJ接口
		//Teacher实现了IG接口
		//那么实际上相当于Teacher类也实现了IJ接口
		IH ih = new Teacher();//(X)Teacher并没有实现IH 
		IJ ij = new Teacher();//Teacher间接实现了IJ接口
	}
}
interface IH {}
interface IJ {}
interface IG extends IJ{}
class Teacher implements IG {}

内部类

一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员【属性,方法,构造器,代码块,内部类】。
内部类最大的特点是可以直接访问私有属性,并且可以体现类与类之间的包含关系。

class Outer{
	//外部类
	class Inner{
		//内部类
	}
}
class Other{
	//外部其他类
}

内部类的分类:

  1. 定义在外部类局部位置上(比如方法内):
    1. 局部内部类(有类名)
    2. 匿名内部类(没有类名)
  2. 定义在外部类的成员位置上:
    1. 成员内部类(没有static修饰)
    2. 静态内部类(static修饰)

局部内部类:
1.可以直接访问外部类的所有成员,包含私有的
2.不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能用修饰符的。但是可以用final来修饰,这是因为局部变量也可以使用final。
3.作用域:仅仅在定义它的方法或代码块中。
4.局部内部类访问外部类成员:直接访问
5.外部类访问局部内部类成员:先创建对象再访问(必须再作用域内)
6.外部其他类不能访问局部内部类(因为局部内部类的地位是一个局部的变量)
7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)
记忆:
1.局部内部类定义在方法中/代码块
2.作用域在方法体或者代码块中
3.本质依然是一个类

匿名内部类:
匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名
1.匿名内部类的基本语法

new 类或者接口(参数列表){
	类体
}
class Outer04{
	//外部类
	private int n1 = 10;//属性
	public void method(){
		//方法
		//如果我想要使用IA接口并创建一个对象
		//传统方式是写一个类,实现该接口,然后创建对象
		IA tiger = new Tiger();
		tiger.cry();
		//如果Tiger类只使用一次,那么需要简化开发
		//使用了匿名内部类来简化开发
		//tiger的编译类型?IA
		//tiger的运行类型?匿名内部类 XXX => Outer04$1
		/*
			看底层 在底层会分配一个类名 Outer04$1
			class XXX implements IA{
				@Override
				public void cry(){
					System.out.println("老虎叫");
				}
			}
		*/
		//jdk底层在创建匿名内部类Outer04$1后,马上创建Outer04$1实例,并且把地址返回给tiger
		//匿名内部类只能使用一次就不可以再使用了,但是tiger可以反复使用
		IA tiger = new IA(){
			@Override
			public void cry(){
				System.out.println("老虎叫");
			}
		};
		tiger.cry();
		System.out.println("tiger的运行类型="+tiger.getClass());
		//输出的是Outer04$1,也就是外部类的名字加上$1

		//演示基于类的匿名内部类
		//1.father 编译类型 Father
		//2.father 运行类型 匿名内部类 Outer04$2
		Father father = new  Father("jack"){};
	}
}
interface IA{//接口
	public void cry();
}
class Tiger implements IA{
	@Override
	public void cry(){
		System.out.println("老虎叫");
	}
}

匿名内部类的语法比较奇特,匿名内部类既是一个类的定义,同时本身也是一个对象,因此从语法上看,即有定义类的特征,也有创建对象的特征

IA tiger = new IA(){
			@Override
			public void cry(){
				System.out.println("老虎叫");
			}
		};
tiger.cry();		
new IA(){
	@Override
	public void cry(){
		System.out.println("老虎叫");
	}
}.cry();		

最佳实践:当做实参直接传递,简洁高效

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{//接口
	public void show();
}

成员内部类:
1.可以直接访问外部类的所有成员,包括私有的
2.可以添加任意访问修饰符(public protected 默认 private),因为它的地址就是一个成员
3.作用域 和外部类的其他成员一样,是整个类体

在这里插入图片描述

内部类:
1.可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
2.可以添加任意访问修饰符(public protected 默认 private)
3.作用域:整个类

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值