static 关键字
有时候希望无论是否产生了对象或无论产生了多少对象的情况下, 某些特定的数据在内存空间里只有一份。
static可以修饰属性、方法、代码块、内部类。
- 类属性作为该类各个对象之间共享的变量。 在设计类时分析哪些属性不因对象的不同而改变 ,将这些属性设置为类属性。相应的方法设置为类方法。
如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法 ,从而简化了方法的调用。
被static修饰后的成员具备以下特点:
- 随着类的加载而加载,由于类只会加载一次,则静态变量在内存中也只会存在一份,存放在方法区的静态域中。
- 优先于对象存在
- 修饰的成员,被所有对象所共享
- 访问权限允许时,可不创建对象,直接被类调用
- 类属性前面省略的是类名,而不是this
被static修饰后的方法:
- 静态方法中,只能调用静态的方法或属性;非静态方法都可以调用
- 在静态方法内,补鞥呢使用this关键字、super关键字
- static修饰的方法不能被重写
- 操作静态属性的方法,通常设置为static
单例(Singleton)设计模式
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例 ,并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的==构造器的访问权限设置为 private==,这样,就不能用 new 操作符在类的外部产生类的对象了,但在==类内部仍可以产生该类的对象==。因为在类的外部开始还无法得到类的对象,==只能====调用该类的某个静态方法==以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的==该类对象的变量也必须定义成静态的==。
// 饿汉式
class Singleton {
// 1. 私有化构造器
private Singleton() {
}
// 2. 内部提供一个当前类的实例
// 4. 此实例也必须静态化
private static Singleton single = new Singleton();
// 3. 提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
return single;
}
}
// 饿汉式
class Bank{
private Bank(){}
public static final Bank instance = new Bank();
}
// 懒汉式
// 懒汉式暂时还存在线程安全问题,讲到多线程时,可修复
class Singleton {
// 1. 私有化构造器
private Singleton() {
}
// 2. 声明当前类对象,没有初始化
// 4. 此实例也必须静态化
private static Singleton single = null;
//3. 提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
if (single == null )
single = new Singleton();
}
return single;
}
}
懒汉式:好处,延迟对象创建;坏处,目前不安全。
饿汉式:好处,是线程安全的;坏处,对象加载时间过长;
单例模式的优点:由于单例模式只生成一个实例,==减少了系统性能开销==,当一个对象的产生 需要比较 多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来 解决。
main()方法说明
- 作为程序的入口
- 也是一个普通的静态方法
- 可以作为与控制台交互的方式,参数为String类型
代码块 (初始化块)
- 代码块或初始化块的作用:对 Java 类或对象进行初始化
- 一个类中代码块若有修饰符则只能被 static 修饰,称为静态代码块(static block) ;没有使用static 修饰的为非静态代码块。
- 静态代码块随着类的加载而执行,而且只执行一次,通常用于初始化 static 的属性。
- 非静态代码块随着类的创建而执行,每次创建均执行。可以对属性初始化。
静态代码块:用 static 修饰的代码块
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
- 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
- 静态代码块的执行要先于非静态代码块。
- 静态代码块随着类的加载而加载,且只执行 一 次。
非静态代码块:没有 static 修饰的代码块
- 可以有输出语句 。
- 可以对类的属性 、 类的声明进行初始化操作 。
- 除了调用非静态的结构外还可以调用静态的变量或方法 。
- 若有多个非静态的代码块,那么按照从上到下的顺序依次执行 。
- 每次创建对象的时候都会执行一次 。 且先于构造器执行 。
class Root{
static{
System.out.println("Root的静态初始化块");
}
{
System.out.println("Root的普通初始化块");
}
public Root(){
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root{
static{
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid的普通初始化块");
}
public Mid(){
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg){
//通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数构造器,其参数值:" + msg);
}
}
class Leaf extends Mid{
static{
System.out.println("Leaf的静态初始化块");
}
{
System.out.println("Leaf的普通初始化块");
}
public Leaf(){
//通过super调用父类中有一个字符串参数的构造器
super("尚硅谷");
System.out.println("Leaf的构造器");
}
}
public class LeafTest{
public static void main(String[] args){
new Leaf();
System.out.println();
new Leaf();
}
}
/*
Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:尚硅谷
Leaf的普通初始化块
Leaf的构造器
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:尚硅谷
Leaf的普通初始化块
Leaf的构造器
*/
class Father {
static {
System.out.println("11111111111");
}
{
System.out.println("22222222222");
}
public Father() {
System.out.println("33333333333");
}
}
public class Son extends Father {
static {
System.out.println("44444444444");
}
{
System.out.println("55555555555");
}
public Son() {
System.out.println("66666666666");
}
public static void main(String[] args) { // 由父及子 静态先行
System.out.println("77777777777");
System.out.println("1************************");
new Son();
System.out.println("2************************");
new Son();
System.out.println("3************************");
new Father();
}
}
/*
11111111111
44444444444
77777777777
1************************
22222222222
33333333333
55555555555
66666666666
2************************
22222222222
33333333333
55555555555
66666666666
3************************
22222222222
33333333333
*/
final 关键字
可以修饰类、方法、变量
- final 标记的类不能被继承 。 提高安全性,提高程序的可读性:String 类 、 System 类 、 StringBuffer 类
- final 标记的方法不能被子类重写。比如: Object 类中的 getClass 。
- final 标记的变量 (成员变量或局部变量) 即称为常量 。 名称大写且只能被赋值一次:final 标记的成员变量必须在声明时或在每个构造器中或代码块中显式赋值,然后才能使用。
- final修饰属性:显示初始化、代码块、构造器;不能在方法中初始化。
- final修饰局部变量、形参。final修饰形参时,表明形参是一个常量,调用此方法时,给常量形参赋一个实参,且只能在方法体内调用,补能重新赋值。
- static final :全局常量
public class Something {
public int addOne( final int x )
return ++x; // 报错,x无法修改
// return x + 1; // 正确
}
}
public class Something {
public static void main(String[] args )
Other o = new Other();
new Something().addOne(o);
}
public void addOne( final Other o )
// o = new Other(); // 报错,形参o的地址无法修改,内部属性可以修改
o.i++; // 正确
}
}
class Other {
public int i;
}
抽象类与抽象方法
- 用 abstract 关键字来修饰一个类, 这个类叫做抽象类 。
- 此类不能实例化
- 抽象类一定有构造器,便于子类实例化调用
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关操作。
- 用 abstract 来修饰一个方法, 该方法叫做抽象方法 。
- 抽象方法:只有方法的声明,没有方法的实现。以分号结束:比如:public abstract void talk()
- 含有抽象方法的类必须被声明为抽象类。
- 抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。==若没有重写全部的抽象方法,仍为抽象类==。
- 不能用 abstract 修饰变量、代码块、构造器;
- 不能用 abstract 修饰 私有方法、静态方法、 final 的方法、 final 的类。
abstract注意点:
- abstract不能修饰属性、构造器、
- abstract私有方法、静态方法、final方法、final的类
是否可以这样理解:抽象类就是比普通类多定义了抽象方法 ,除了不能直接进行类的实例化操作之外,并没有任何的不同?对~
模板方法设计模式 TemplateMethod
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
解决的问题:
- 当功能内部一部分实现 是 确定 的 一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去 实现 。
- 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
模板方法设计模式是编程中经常用得到的模式 。 各个框架 、 类库中都有他的影, 比如常见的有:
- 数据库访问的封装
- Junit 单元测试
- JavaWeb 的 Servlet 中关于 doGet/doPost 方法调用
- Hibernate 中模板程序
- Spring 中 JDBCTemlate 、 HibernateTemplate 等
接口
一方面 有时 必须从几个类中派生出一个子类 继承它们所有的属性和方法 。 但是 Java 不支持多重继承 。 有了接口就可以得到多重继承的 效果 。
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是 要 则必须能 ……”的思想。 ==继承是一个是不是的关系,而接口实现则是能不能的关系==。
==接口的本质是契约,标准,规范==,就像我们的法律一样。制定好后大家都要遵守。
JDK7.0 接口 interface 是==抽象方法==和==常量值==定义的集合 。
JDK8.0 还可以定义静态方法,默认方法:
- 接口中定义的静态方法,只能通过接口来调用。
- 默认方法使用 default 关键字修饰。可以通过实现类对象来调用。
我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。重写接口中的默认方法,调用的为重写后的方法。- 类优先原则:如果子类(实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,在子类没有重写的情况下,默认调用父类的方法。
- 接口冲突:如果实现类实现了多个接口,而多个接口存在同名同参数的默认方法,在实现类没有重写的情况下,会出现接口冲突;这就需要必须重写此方法。
- 调用接口中被重写的方法。
- 默认方法使用 default 关键字修饰。可以通过实现类对象来调用。
接口的特点:
- 用 interface 来定义 。接口和类是并列的结构。
- 接口中的所有成员变量都 默认是由 public static final 修饰的。可以省略不写。
- 接口中的所有抽象方法 都 默认是由 public abstract 修饰的 。
- 接口中没有构造器 。
- 接口采用多继承机制。
- 定义 Java 类的语法格式: 先写 extends ,后写 implements:class SubClass extends SuperClass implements InterfaceA
- 一 个类可以实现多个接口,接口也可以继承其它接口 。
- 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。
- 接口的主要用途就是被实现类实现(面向接口编程)
- 与继承关系类似,接口与实现类之间存在多态性
- 接口和类是并列关系 或者可以理解为一种特殊的类 。 从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义(JDK7.0 及之前) 而没有变量和方法的实现。
interface A {
int x = 1;
}
class B {
int x = 1;
}
class C extends B implements A {
public void pX() {
// System.out.println(x);// 编译不通过
System.out.println(super.x);// 1
System.out.println(A.x);// 0
}
public static void main(String[] args ){
new C().pX();
}
}
interface Playable {
void play();
}
interface Bounceable {
void play();
}
interface Rollable extends Playable, Bounceable {
Ball ball = new Ball("PingPang");
}
class Ball implements Rollable {
private String name;
public String getName() {
return name;
}
public Ball(String name )
this.name = name;
}
public void play() {
ball = new Ball("Football");// ball 是 static final修饰的,无法重新赋值
System.out.println(ball.getName());
}
}
内部类
成员内部类:
- 作为外部类的成员:
- 调用外部类的结构,Person.this.属性
- static修饰
- 4中权限修饰符
- 作为一个类:
- 定义属性、方法、构造器
- final修饰
- abstract修饰
- 编译以后生成 OuterClass$InnerClass.class 字节码文件 也适用于局部内部类
实例化成员内部类的对象
// 静态的成员内部类
Person.Dog dog = new Person.Dog();
// 非静态的成员内部类
Persion p = new Person();
Person.Bird bir = p.new Bird();
注意:
1.
非 static 的成员内部类中的成员不能声明为 static 的,只有在外部类或 static 的成员内部类中才可声明 static 成员 。
2.
外部类访问成员内部类的成员需要”内部类.成员”或”内部类对象.成员”的方式
3.
成员内部类可以直接使用外部类的所有成员 包括私有的数据
4.
当想要在外部类的静态成员部分使用内部类时可以考虑内部类声明为静态的
抽象类可以实现接口。抽象类可以继承非抽象的类。
在局部内部类的方法中,如果调用外部类声明的方法中局部变量,则要求此局部变量声明final
JDK8.0可以省略final关键字。