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方法的形式:public static void main(String[] args){}
代码块又称为初始化块,属于类中的成员【即 是类的一部分】,类似与方法,将逻辑语句封装在方法体中,通过{}包围起来。
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
基本语法:
[修饰符]{
代码
};
注意:
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.所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
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修饰。
当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。(格式:访问修饰符 final 返回类型 方法名)
当不希望类的某个属性的值被修改,可以用final修饰。(格式:public final double TAX_RATE=0.08)
当不希望某个局部变量被修改,可以使用final修饰(格式: final double TAX_RATE=0.08)
final修饰的属性又叫常量,一般用 XX_XX_XX来命名(因为该属性的值不可被更改了)
final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一【选择一个位置赋初值即可】:
1. 定义时:如 public final double TAX_RATE=0.08
2. 在构造器中
3. 在代码块中
如果final修饰的属性是静态的,则初始化的位置只能是:
final类不能继承,但是可以实例化对象。
如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。
final不能修饰构造方法(即构造器)
final 和 static 往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理。
包装类(Integer,Double,Float,Boolean等都是final),String也是final类
父类方法的不确定性:当父类的某些方法需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类。
所谓抽象方法就是没有实现的方法。
所谓没有实现就是指,没有方法体。
当一个类中存在抽象方法时,需要将该类声明为abstract类
一般来说,抽象类会被继承,由其子类来实现抽象方法
抽象类的介绍:
接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来:
语法:
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 接口{
自己属性;
自己方法;
必须实现的接口的抽象类
}
接口的多态特性
多态传递特性:
/*
演示多态传递现象
*/
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.可以直接访问外部类的所有成员,包含私有的
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.作用域:整个类