1.static关键字
(1)static属性
例如要写一个中国人的类,那么不必为每个对象都分配一个中国国籍对象。而是所有人共用一个。
设置时通过类设置,访问时也通过类访问。如下:
class Chinese {
public static String country;
}
public class Test {
public static void main(String[] args) {
Chinese.country = "中国";
System.out.println(Chinese.country);
}
}
实例变量:
只有实例化之后才能使用,属于实例化对象的一部分,不能共用。
类变量:
使用类名调用,是类的一部分,被这个类的所有实例化对象所共享。也可以叫做静态变量。
如下代码中,country属于类变量,而name属于实例变量。
class Chinese {
public static String country;
public String name;
}
所以如果想让一个类的所有实例共享数据,则需使用类变量。
(2)static方法
如果方法与调用者无关,则这样的方法通常被声明为类方法。由于不需要创建对象就可以调用方法,从而简化了方法的调用过程。
也称为静态方法。
静态方法常应用于工具类。如下是一个判断字符串不为空的工具类。
class Tool {
public static boolean isEmpty(String s) {
boolean flag = false;
if (s != null && !s.equals(""))
flag = true;
return flag;
}
}
(3)static关键字总结
使用范围
在Java类中,可用static修饰属性、方法、代码块、内部类。
被修饰后的成员具备以下特点:
- 随着类的加载而加载。
- 优先于对象存在。
- 被所有对象共享。(可用于计数)
- 访问权限允许时,可不创建对象,直接被类调用。
如下,可用静态方法来计数new了多少个对象。
class CountObject {
static int count;
public CountObject() {
CountObject.count += 1;
}
public static void show() {
System.out.println(CountObject.count);
}
}
注意:
- 使用类变量时要慎重。
- static方法内部不能有this和super。
- 重载的方法需要同时为static的或者非static的。
(4)单例(Singleton)设计模式
所谓单例设计模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
应用场景:
实例化对象的创建要消耗大量的时间和资源。或频繁new对象没有意义。
单例设计模式之一:饿汉式
分为以下三个步骤:
- 构造方法私有化。无法再通过new来创建该类的对象。
- 创建一个该类型的私有类变量。
- 通过一个public静态方法访问这个私有类变量。
如下:
class Example {
private Example() {} //重写构造方法
private static Example e = new Example(); //创建唯一实例作为私有类变量
public static Example getInstance() {
return e;
} //为类外访问该唯一实例提供public方法
}
单例设计模式之二:懒汉式
它与饿汉模式的区别是,它一开始没有new对象(null),直到第一次调用才new出一个对象,而之后所有调用都是调用这个唯一的对象。
所以,这两种单例模式的根本区别在于,对象何时创建。
懒汉式的步骤为:
- 构造方法私有化。
- 创建该类型的一个私有变量,但赋值为null。
- 创建一个public静态方法访问这个私有变量。如果当前为null,则new一个;如果当前不是null,则直接返回已有的私有变量。
如下:
class Example {
private Example() {}
private static Example e = null;
public static Example getInstance() {
if (e == null) {
e = new Example();
}
return e;
}
}
懒汉式存在线程安全问题,待续。
2.main方法的语法
public static void main(String[] args) {}
- JVM需要调用类的main方法,所以该方法必须是public。
- 执行main方法时不需要创建对象,它是一个类方法,所以为static。
- main方法接受一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。
如下:
public class TestMain {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++)
System.out.println(args[i]);
}
}
编译后,若执行如下命令:
java TestMain.java aa bb cc
则打印输出:
aa
bb
cc
3.类的成员之四:初始化块(代码块)
作用:
对Java对象进行初始化。
如下:
public class Test {
String name;
public Test() {
System.out.println("构造方法");
}
{
System.out.println("代码块");
}
public static void main(String[] args) {
Test t = new Test();
}
}
则输出为:
代码块
构造方法
执行顺序为:
- 类的属性的默认初始化和显示初始化。
- 执行代码块的代码。
- 执行构造器的代码。
如果有多个代码块,则顺序执行:
public class Test {
{
System.out.println("代码块1");
}
{
System.out.println("代码块2");
}
{
System.out.println("代码块3");
}
public static void main(String[] args) {
Test t = new Test();
}
}
输出为:
代码块1
代码块2
代码块3
静态代码块
被static修饰的代码块称为静态代码块。代码块只有static一个修饰符。
静态代码块中只能对静态属性和静态方法进行初始化。
无论new多少个对象,静态代码块只执行一次。如下:
public class Test {
static int i;
static void showInfo() {
System.out.println("静态方法");
}
static
{
i = 1;
showInfo();
System.out.println("执行静态代码块");
}
public static void main(String[] args) {
Test t1 = new Test();
Test t2 = new Test();
}
}
执行结果为:
静态方法
执行静态代码块
静态代码块一般用于初始化类的静态属性。
对比
非静态代码块:没有static修饰的代码块
- 可以有输出语句。
- 可以对类的属性进行初始化操作。
- 可以调用静态或非静态的变量或方法。
- 若有多个非静态的代码块,按照从上到下的顺序依次执行。
- 每次创建对象的时候,都会执行一次。
- 优先于构造器执行。
静态代码块:用static修饰的代码块
- 不可以对非静态的属性初始化,即不可以调用非静态的属性和方法。
- 若有多个静态代码块,则按顺序执行。
- 静态代码块优先于非静态代码块执行。
- 静态代码块只执行一次。
在实际开发中,static静态代码块用来初始化类的静态属性。
在匿名内部类中,用代码块代替构造方法。
class Animal {
String name;
void Animal() {
this.name = "xiaohua";
}
void printInfo() {
System.out.println("Animal");
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal() {
{
super.name = "xiaoming";
}
@Override
void printInfo() {
System.out.println("Test");
}
};
System.out.println(a.name);
a.printInfo();
}
}
则输出为:
xiaoming
Test
4.final关键字
使用final修饰类、属性和方法,表示“最终”。
- final修饰的类不能被继承。例如String类、System类、StringBuffer类。
- final修饰的方法不能被子类重写。例如Object类中的getClass()。
- final修饰的变量(成员变量或局部变量)则为常量,注意一般将其名称大写,如有多个单词则用下划线连接。常量必须显式赋值且只能赋值一次。
- final static一起修饰的变量就叫做全局常量。
5.抽象类
有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
abstract关键字:
- 用abstract修饰的类叫做抽象类。
- 用abstract修饰的方法叫做抽象方法。抽象方法只包含方法的声明,不包含方法的实现,以分号结束。
abstract int abstractMethod(int a);
- 含有抽象方法的类必须被声明为抽象类。
- 抽象类不能被实例化。抽象类是用来作为父类被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,则仍为抽象类。
- 不能用abstract修饰属性、私有方法、构造器、静态方法、final的方法。
抽象类应用
抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提供具体实现的对象的类。
抽象类不能实例化,new AbstractClass()
是非法的。
模板方法设计模式(TemplateMethod)
抽象类体现的是一种模板模式的设计。抽象类作为多个子类的通用模板,子类在抽象类的基础上进行改造、扩展,但子类总体上会保留抽象类的行为方式。
abstract class Template {
public abstract void code();
public final void getTime() {
long start = System.currentTimeMillis();
code();
long end = System.currentTimeMillis();
System.out.println("执行时间为:" + (end - start));
}
}
class Code1 extends Template{
public void code() {
int k = 1;
for (int i = 0; i <1000000; i++)
{
k += 1;
}
}
}
public class Test {
public static void main(String[] args) {
Code1 c = new Code1();
c.getTime();
}
}
当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
6.接口(interface)
- 有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是Java不支持多重继承。利用接口可以实现多重继承的效果。
- 接口是抽象方法和常量值的定义的集合。
- 从本质上讲,接口是一种特殊的抽象类,这种抽象类只包含常量和方法的定义,而没有变量和方法的实现。
- 接口类的实现:
class SubClass implements intergaceA{}
- 一个类可以实现多个接口,接口也可以继承其他接口。
接口的特点:
- 用interface来定义;
- 接口中的所有成员变量都默认是由public static final修饰的,即全局静态常量;
- 接口中的所有方法都默认是由public abstract修饰的;
- 接口没有构造器,不能new;
- 接口采用多层继承机制;
- 实现接口的类必须提供接口中所有方法的具体实现内容(大括号),方可实例化。否则仍为抽象类,要在类前加abstract才不会报错;
- 接口的主要用途就是被实现类实现;
- 与继承关系类似,接口与实现类之间存在多态性;
- 语法格式:险些extends,后写implements。
一般来讲,抽象类是对一类事物的高度抽象,其中既有属性也有方法。
接口是对方法的抽象,也就是对一系列动作的抽象。
工厂方法(FactoryMethod)
FactoryMethod模式是设计模式中应用最广泛的模式。
在面向对象的编程中,对象的创建工作非常简单,对象的创建时机却很重要。FactoryMethod通过面向对象的手法,将所要创建的具体对象的创建工作延迟到了子类,从而提供了一种扩展的策略,较好地解决了这种紧耦合的关系。
7.类的成员之5:内部类
- 若一个类位于另一个类的内部,则前者为内部类,后者为外部类。
- Inner class 可以使用外部类的私有数据。若外部类要访问内部类中的成员需要以如下形式:内部类.成员 或 内部类对象.成员。
public class External {
private int id;
class A{
public void setExternal() {
id = 1;
}
}
public void setInfo() {
new A().setExternal();
}
public void showInfo() {
System.out.println(this.id);
}
}
内部类的特点:
- 可以是final的;
- 可以是private和protected的;
- 可以为static的,但此时不能再使用外部类的非 static 成员变量;
- 可以声明为abstract的,因此可以被其他的内部类继承;
- 非static的内部类中的成员不能声明为static的,只有在外部类或static的内部类中才可声明static成员。
内部类主要是解决Java不能多重继承的问题。
可以使用内部类来变相实现类的多重继承,可以同时继承多个类。
比如说想在A类中同时重写B的一个方法和C的一个方法。则:
public class Test {
public static void main(String[] args) {
A a = new A();
a.testB();
a.testC();
}
}
class A {
public void testB() {
new InnerB().testB();
}
public void testC() {
new InnerC().testC();
}
private class InnerB extends B {
@Override
public void testB() {
System.out.println("重写B中的方法");
}
}
private class InnerC extends C {
@Override
public void testC() {
System.out.println("重写C中的方法");
}
}
}
class B {
public void testB() {
}
}
class C {
public void testC() {
}
}
输出为:
重写B中的方法
重写C中的方法
所以,内部类的最大作用是实现多重继承。