static
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。
实例变量是属于类的每个具体实例的,每个实例的实例变量值都不同;类变量是属于类的,一个类的所有实例都共享同一个类变量。类方法也类似。
- 使用范围:属性、方法、代码块、内部类
- 特点:
- 随着类的加载而加载
- 优先于对象存在
- 修饰的成员被所有实例对象共享
- 访问权限允许时,可不创建实例对象,直接被类调用
static变量内存解析
static方法
- 在
static
方法中,只能访问类的static
修饰的属性或方法,不能访问非static
结构,不能有this
、super
等引用 static
方法不能被重写
练习:设计一个类,可以自动生成账号。
private static int num = 0;//设置一个静态初始值作为生成账号的数字
public int setNum(){
num++;
return num;
}
单例模式
- 饿汉式:先生成静态实例,等待被调用
class Singleton{
private Singleton(){} //私有化构造器,以防被调用
//内部要创建一个静态实例供调用
private static Singleton singleton = new Singleton();
//提供公共的静态方法,返回当前类对象,供外部使用。为什么这个还要静态的?我猜测是以防被重写。
//毕竟单例模式只向外提供调用实例的借口,不想被操作
public static Singleton getInstance(){
return singleton;
}
}
- 懒汉式:等到被调用的时候再生成静态实例
class Singleton{
private Singleton(){} //私有化构造器
//提供静态实例
private static Singleton singleton;
//提供调用接口
public static Singleton getInstance(){
if (singleton == null){ //先检查是否有实例
singleton = new Singleton();//没有就创建
}
return singleton;//有就返回
}
}
- 单例模式优点:只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
- 应用场景:
- 网站的计数器,一般也是单例模式实现,否则难以同步。
- 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
- 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都生成一个对象去读取。
main方法
- 由于Java虚拟机需要调用类的
main()
方法,所以该方法的访问权限必须是public
,又因为Java虚拟机在执行main()
方法时不必创建对象,所以该方法必须是static
的,该方法接收一个String
类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。 - 又因为
main()
方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
代码块
- 作用:对Java类或对象进行初始化
- 分类:
- 静态代码块:
static
修饰,常用于初始化static
的属性,不能操作非静态属性方法,先于非静态代码块执行,随着类的加载而加载,且只执行一次 - 非静态代码块:先于构造器执行
- 静态代码块:
- 程序中成员变量赋值的执行顺序:
final
final
标记的:- 类不能被继承
- 方法不能被重写
- 变量只能被赋值一次
static final
:全局常量
抽象类与抽象方法
- 用
abstract
修饰,只有方法声明没有方法实现的是抽象方法;包含抽象方法的叫抽象类 - 抽象类不能被实例化,只能用来继承,并且被子类重写抽象方法。如果没有重写全部的抽象方法,则子类仍为抽象类
- 私有方法、静态方法、final方法不能被重写,所以不能用
abstract
修饰
接口
- 接口就是规范,定义的是一组规则。接口的本质是契约,标准,规范,制订好后就要遵守。继承是一个“是不是”的关系,接口则是“能不能”的关系
- 接口是抽象方法和常量值定义的集合。从JDK8开始,可以有静态属性、
final
属性、静态默认方法,特点:- 用
interface
来定义 - 接口中所有属性默认是由
public static final
修饰的 - 接口中所有抽象方法默认是由
public abstract
修饰的 - 接口中没有构造器
- 接口采用多继承机制
- 接口也可以继承其它接口
- 用
- 实现接口的类必须提供接口中所有方法的具体内容,否则仍为抽象类
- 接口可以看作是特殊的类,而且是“父类”,可以与实现类之间存在多态关系,即可以声明一个接口的引用,指向实现类对象。
- 接口的应用:代理模式和工厂模式(还没研究,待学习)
练习:找出下列程序中的错误
ball = new Ball("Football");
这句错了。因为接口里都是常量,不论是属性还是方法,都不能被修改
- Java 8中可以为接口添加静态方法和默认方法。可以通过接口直接调用静态方法。默认方法使用
default
关键字修饰。可以通过实现类对象来调用。 - 若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接口时,会出现接口冲突。解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突。
- 若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守:类优先原则。接口中具有相同名称和参数的默认方法会被忽略。
内部类
- Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
- 分类:
- 成员内部类:
static
成员内部类和非static
成员内部类 - 局部内部类
- 匿名内部类
- 成员内部类:
成员内部类
- 成员内部类:
- 一方面,作为外部类的成员:
- 调用外部类的结构,包括
private
- 可以被static修饰
- 可以被4种不同的权限修饰
- 调用外部类的结构,包括
- 另一方面,作为一个类:
- 类内可以定义属性、方法、构造器等
- 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
- 可以被abstract修饰
- 一方面,作为外部类的成员:
- 非
static
内部类中的成员不能声明为static
的,只有在外部类或static
内部类中才可以声明static
成员 - 编译以后生成OuterClass$InnerClass.class字节码文件(也适用于局部内部类)
- 代码示例:
成员内部类
class Person{
String name = "小明";
int age;
public void eat(){
System.out.println("人:吃饭");
}
static class Dog{ //静态成员内部类
String name;
int age;
public void show(){
System.out.println("卡拉是条狗");
}
}
class Bird{ //非静态成员内部类
String name = "杜鹃";
public Bird(){ //构造器
}
public void sing(){
System.out.println("我是一只小小鸟");
Person.this.eat();//调用外部类的非静态属性
eat();
System.out.println(age);
}
public void display(String name){
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(Person.this.name);//外部类的属性
}
}
}
局部内部类
- 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类
- 但是它的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类的父类或父接口类型
- 局部内部类可以使用外部方法的局部变量,但是必须是
final
的。局部内部类和局部变量地位类似,不能使用public
,protected
,缺省,private
- 局部内部类不能使用
static
修饰,因此也不能包含静态成员 - 使用中不常见,了解即可
- 代码示例:
public void method(){//方法体内
class AA{//局部内部类
}
}
{//代码块内
class BB{//局部内部类
}
}
public Person(){//构造器内
class CC{//局部内部类
}
}
匿名内部类(没太懂)
- 匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一 个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或 实现一个类。
- 格式:
new 父类构造器(实参列表)|实现接口(){
//匿名内部类的类体部分
});
- 匿名内部类的特点:必须继承父类或实现接口;只能有一个对象;对象只能使用多态形式引用
- 代码示例:
interface A{
public abstract void fun1();
}
public class Outer {
public static void main(String[] args) {
new Outer().callInner(new A() {
//接口不能new,但此处比较特殊是子类对象实现接口,只不过没有为对象取名
public void fun1() {
System.out.println("implement for fun1");
}
});// 两步写成一步了
}
public void callInner (A a){
a.fun1();
}
}