static关键字
-
引入:当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。
-
类属性作为该类各个对象之间共享的变量。在设计类时,分析哪些属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法。
-
如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用。
-
使用范围:
- 在Java类中,可用static修饰属性、方法、代码块、内部类
- 只能在静态类型类或顶级类型类中才能声明静态方法
-
被修饰后的成员具备以下特点:
- 随着类的加载而加载
- 优先于对象存在
- 修饰的成员,被所有对象所共享
- 访问权限允许时,可不创建对象,直接被类调用
-
static修饰属性:静态属性(类属性)
- 类变量(类属性)由该类的所有实例共享
- 静态属性随着类的加载而加载,可以通过“类.静态属性”的方式进行调用
- 可以通过类名调用静态属性而不能调用非静态属性,可以通过对象调用静态属性和非静态属性
-
static修饰方法:静态方法(类方法)
- 没有对象的实例时,可以用类名.方法名()的形式访问由static修饰的类方法。在static方法内部只能访问类的static修饰的属性或方法,不能访问类的非static的结构。
- 因为不需要实例就可以访问static方法,因此static方法内部不能有this和super。
- static修饰的方法不能被重写
-
main()方法的说明:
- 由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。
- 又因为main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情况,我们在之前的例子中多次碰到
类的成员之四:代码块
-
代码块(或初始化块)的作用: 对Java类或对象进行初始化
-
代码块(或初始化块)的分类:
- 一个类中代码块若有修饰符,则只能被static修饰,称为静态代码块
- 没有使用static修饰的,为非静态代码块。
-
static代码块通常用于初始化static的属性
-
静态代码块:用static 修饰的代码块
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
- 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
- 静态代码块的执行要先于非静态代码块。
- 静态代码块随着类的加载而加载,且只执行一次。
-
非静态代码块:没有static修饰的代码块
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 除了调用非静态的结构外,还可以调用静态的变量或方法。
- 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
- 每次创建对象的时候,都会执行一次。且先于构造器执行。
-
程序中成员变量赋值的执行顺序
final关键字
-
在Java中声明类、变量和方法时,可使用关键字final来修饰,表示“最终的”。
-
final标记的类不能被继承。提高安全性,提高程序的可读性。
- 比如:String类、System类、StringBuffer类
-
final标记的方法不能被子类重写。
- 比如:Object类中的getClass()。
-
final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。
- final标记的成员变量必须在声明时或在每个构造器中或代码块中显式赋值,然后才能使用。
-
static final:全局常量
抽象类和抽象方法
-
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
-
用abstract关键字来修饰一个类,这个类叫做抽象类。
abstract class Person{ }
-
用abstract来修饰一个方法,该方法叫做抽象方法。
- 抽象方法:只有方法的声明,没有方法的实现。以分号结束:
public abstract void eat();
-
含有抽象方法的类必须被声明为抽象类。
-
抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类
-
不能用abstract修饰变量、代码块、构造器;
-
不能用abstract修饰私有方法、静态方法、final的方法、final的类。
-
抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提供具体实现的对象的类。
-
多态的应用:模板方法设计模式(TemplateMethod)
- 抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
- 解决的问题:
- 当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
- 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式
- 模板方法设计模式是编程中经常用得到的模式。各个框架、类库中都有他的影子,比如常见的有:
- 数据库访问的封装
- Junit单元测试
- JavaWeb的Servlet中关于doGet/doPost方法调用
- Hibernate中模板程序
- Spring中JDBCTemlate、HibernateTemplate等
-
匿名对象的使用
public class AbstraceTest { public static void main(String[] args) { //非匿名的类(Student)匿名对象 method2(new Student());//吃饭 //非匿名的类(Student)非匿名的对象(s) Student s = new Student(); method1(s);//吃饭 //创建了一个匿名子类的对象p Person p = new Person() { @Override public void eat() { System.out.println("匿名吃饭"); } }; method1(p);//匿名吃饭 //创建匿名子类的匿名对象 method1(new Person() { @Override public void eat() { System.out.println("匿名子类吃饭"); } });//匿名子类吃饭 } public static void method1(Person person) { person.eat(); } public static void method2(Student student) { student.eat(); } } abstract class Person{ public abstract void eat(); } class Student extends Person{ @Override public void eat() { System.out.println("吃饭"); } }
接口(interface)
-
一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
-
另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。
-
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则必须能...”的思想。继承是一个"是不是"的关系,而接口实现则是 "能不能"的关系。
-
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。
-
接口(interface)是抽象方法和常量值定义的集合。
-
接口的特点:
- 用interface来定义。
- 接口中的所有成员变量都默认是由public static final修饰的。(JDK7及以前)
- 接口中的所有抽象方法都默认是由public abstract修饰的。 (JDK7及以前)
- 接口中还支持静态方法和默认方法。(JDK8及以后)
- 接口中没有构造器。
- 接口采用多继承机制。
//接口的定义 public interface Person{ //常量 public static final int MAX_NUM = 1; //抽象方法 public abstract void show(); //默认方法 public default void eat() { System.out.println("eat"); } //静态方法 public static void work() { System.out.println("work"); } }
-
定义Java类的语法格式:先写extends,后写implements
例如:class A extends B implements C{ }
-
一个类可以实现多个接口,接口也可以继承其它接口。
-
实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。
-
接口的主要用途就是被实现类实现。(面向接口编程)
-
与继承关系类似,接口与实现类之间存在多态性
-
接口和类是并列关系,或者可以理解为一种特殊的类。
-
接口的应用:
- 代理模式:代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问
- 应用场景:
- 安全代理:屏蔽对真实角色的直接访问。
- 远程代理:通过代理类处理远程方法调用(RMI)
- 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
- 分类
- 静态代理(静态定义代理类)
- 动态代理(动态生成代理类)
- 应用场景:
- 工厂模式:实现了创建者与调用者的分离,即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
- 代理模式:代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问
-
接口与抽象类之间的对比
| No. | 区别点 | 抽象类 | 接口|
| ---- | ---- | ---- | ----|
| 1 |定义 | 包含抽象方法的类 | 主要是抽象方法和全局常量的集合|
| 2 |组成 |构造方法、抽象方法、普通方法、常量、变量 | 常量、抽象方法、(jdk8.0:默认方法、静态方法) |
| 3 |使用 |子类继承抽象类(extends)| 子类实现接口(implements)|
|4 |关系 |抽象类可以实现多个接口 |接口不能继承抽象类,但允许继承多个接口|
| 5 |常见设计模式 |模板方法 |简单工厂、工厂方法、代理模式|
| 6 |对象 |都通过对象的多态性产生实例化对象||
| 7 |局限| 抽象类有单继承的局限 |接口没有此局限|
| 8 |实际| 作为一个模板|是作为一个标准或是表示一种能力|
| 9 | 选择| 如果抽象类和接口都可以使用的话,优先使用接口, |因为避免单继承的局限| -
在开发中,常看到一个类不是去继承一个已经实现好的类,而是要么继承抽象类,要么实现接口。
JDK 8关于接口的改进
- Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
- 静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。
- 默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。
- 比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认方法
- 若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接口时,会出现:接口冲突。
- 解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突。
- 若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守:类优先原则。接口中具有相同名称和参数的默认方法会被忽略。
类的成员之五:内部类
-
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
-
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。
-
内部类一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
- 内部类的名字不能与包含它的外部类类名相同;
-
分类:
- 成员内部类(static成员内部类和非static成员内部类)
- 局部内部类(不谈修饰符)、匿名内部类
-
成员内部类作为类的成员的角色:
- 和外部类不同,内部类还可以声明为private或protected。
- 可以调用外部类的结构
- 内部类可以声明为static的,但此时就不能再使用外层类的非static的成员变量。
-
成员内部类作为类的角色:
- 可以在内部定义属性、方法、构造器等结构
- 可以声明为abstract类 ,因此可以被其它的内部类继承
- 可以声明为final的
- 编译以后生成OuterClass$InnerClass.class字节码文件(也适用于局部内部类)
-
注意
- 非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员内部类中才可声明static成员。
- 外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式
- 成员内部类可以直接使用外部类的所有成员,包括私有的数据
- 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的
-
如何声明局部内部类
class 外部类{ 方法(){ class 局部内部类{ } } { class 局部内部类{ } } }
-
如何使用局部内部类
- 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类
- 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号,以及数字编号。
- 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类。
- 局部内部类可以使用外部类的成员,包括私有的。 局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局部变量的声明周期不同所致。
- 局部内部类和局部变量地位类似,不能使用public,protected,缺省,private
- 局部内部类不能使用static修饰,因此也不能包含静态成员
-
匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
-
格式:
new 父类构造器(实参列表)|实现接口(){ //匿名内部类的类体部分 }
-
匿名内部类的特点
-
匿名内部类必须继承父类或实现接口
-
匿名内部类只能有一个对象
-
匿名内部类对象只能使用多态形式引用
public class AbstraceTest {
public String name;
public int age = 10;
public void eat() {
}
//静态成员内部类
static class Person{
public String name;
public int age;
public void eat() {
}
}
//非静态成员内部类
public class Student{
public String name;
public int age;
public void eat() {
AbstraceTest.this.age = 11;
}
public void show() {
//调用内部类的属性
System.out.println(this.name);
//调用外部类的属性
System.out.println(AbstraceTest.this.name);
//调用内部类的方法
this.eat();
//调用外部类的方法
AbstraceTest.this.eat();
}
}
//开发中的使用
public Comparable getComparable() {
int num ;
class MyComparable implements Comparable{
@Override
public int compareTo(Object o) {
// TODO 自动生成的方法存根
return 0;
}
}
return new MyComparable();
}
}
总结
/**
* static关键字的使用
* 1.static:静态的
* 2.static可以用来修饰:属性,方法,代码块,内部类
* 3.使用static修饰属性:静态变量(类变量)
* 3.1属性,按是否使用static修饰,属性分为静态属性vs非静态属性(实例变量)
* 实例属性:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。
* 当修改其中的一个对象中的 非静态属性时,不会导致其他对象中同样的属性值的修改
* 静态属性:我们创建了类的多个对象,多个对象共享同一个静态属性。
* 当通过类或某个对象修改静态属性时, 会导致其他对象调用此静态属性时,值是修改过的。
* 3.2补充说明:
* ①:静态属性随着类的加载而加载,可以通过“类.静态属性”的方式进行调用
* ②:静态属性的加载要早于对象的创建
* ③:由于类只会加载一次,则静态属性在内存中也只会存在一份(存放在方法区的静态域中)
* ④:可以通过 类名 调用静态属性而不能调用非静态属性
* 可以通过 对象 调用静态属性和非静态属性
* 3.3举例:System.out Math.PI
* 4.使用static修饰方法:静态方法
* 4.1随着类的加载而加载,可以通过“类.静态方法”的方式进行调用
* 4.2静态方法中只能调用静态的方法和属性
* 4.3非静态方法中可以调用非静态的属性和方法,也可以调用静态的属性和方法
* 4.4可以通过 类名 调用静态方法而不能调用非静态方法
* 可以通过 对象 调用静态方法和非静态方法
* 5.static注意点:
* 5.1在静态的方法内,不能使用this关键字,super关键字
* 5.2关于静态属性和静态方法的使用,都会从生命周期的角度去理解
* 6.开发中如何确定一个属性是否要声明为static的?
* >属性是可以被多个对象所共享的,不会随着对象的不同而不同 >类中的常量也经常被设置为static的
* 开发中如何确定一个方法是否要声明为static的?
* >操作静态属性的方法,通常设置为static的
* >工具类中的方法,习惯上声明为static的。比如:Math,Arrays,Collections
* 7.main()方法的使用说明:
* >main()方法作为程序的入口
* >main()方法也是一个普通的静态方法
* >mian()方法可以作为我们与控制台交互的方法
*
* 类的成员之四:代码块
* 1.代码块的作用:初始化类,对象
* 2.代码块的修饰符只能是static
* 3.分类:静态代码块vs非静态代码块
* 4.静态代码块
* >内部可以有输出语句
* >随着类的加载而执行,而且只执行一次
* >作用:初始化类的信息
* >如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
* >静态代码块的执行要有优先于非静态代码块的执行
* >静态代码块内只能调用静态属性,静态的方法。不能调用非静态的属性和方法 5.非静态代码块
* >内部可以有输出语句
* >随着对象的创建而执行
* >每次创建一个对象,就执行一次非静态代码块
* >作用:可以在创建对象时,对对象的属性等进行初始化
* >如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
* >非静态代码块内可以调用静态属性,静态的方法。也能调用非静态的属性和方法
*
* 对属性赋值的位置:
* ①默认初始化
* ②显式初始化
* ③构造器中初始化
* ④通过“对象.属性”或“对象.方法”的方式进行复制
* ⑤在代码块中进行初始化
* 顺序:① - ②/⑤ - ③ - ④
* 补从与说明:对于只有非静态代码块和非静态属性来说②和⑤谁在类的前面先执行谁。
* 对于静态代码块和静态属性来说来说②和⑤谁在类的前面先执行谁。
* 对于静态代码块和非静态代码块和静态属性来说。非静态代码块在最后执行且不受位置的影响
* 关于代码块与构造器的执行顺序: 举例:爷,父,儿三者之间的关系。主函数中调用子类的构造器 。
* 输出顺序:爷的静态代码块 - 父的静态代码块 - 儿的静态代码块
* - 爷的非静态代码块 - 爷的无参构造器 - 父的非静态代码块
* - 父的无参构造器 - 儿的非静态代码块 - 儿的构造器
*
* final关键字
* 1.final可以用来修饰的结构:类,方法,变量
* 2.final用来修饰一个类:此类不能被其他类继承。
* 比如:String类,System类
* 3.final用来修饰方法:表明此方法不可以被重写 比如:Object类中getClass();
* 4.final用来修饰变量:此时的变量就称为是一个常量
* 4.1 final修饰属性:可以考虑赋值的位置有:显式初始化,代码块中初始化,构造器中初始化(属性不能是静态的)
* 4.2 final修饰变量:final可以修饰形参和实参,但是一旦被赋值后这个变量可以被使用但不能改变值
* static和final在一起通常修饰和全局变量
*
* abstract关键字
* 1.abstract:抽象的
* 2.abstract可以用来修饰的结构:类,方法
* 3.abstract修饰类:抽象类
* >此类不能被实例化
* >抽象类中一定有构造器,便于子类实例化时调用
* >开发中都会提供抽象类的子类,让子类对象实例化,完成对抽象类的使用
* 4.abstract修饰方法:抽象方法
* >抽象方法只有方法的声明,没有方法体
* >包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象的方法
* >若子类重写了父类中的所有的抽象的方法后,此子类方可实例化
* 若子类没有重写父类中的所有抽象方法,则此子类也是一个抽象类,需使用abstract修饰
* 5.注意点
* >abstract不能修饰:属性,构造器等结构
* >abstract不能修饰私有方法,静态方法,final的方法,final的类
*
* 接口的使用
* 1.接口使用interface定义
* 2.Java中,接口和类是并列的两个结构
* 3.如何定义接口:定义接口中的成员
* 3.1 JDK7及以前:只能定义全局常量和抽象方法
* >全局常量:public static final修饰的。书写时可以将修饰符省略不写
* >抽象方法:public abstract修饰的。书写时可以将修饰符省略不写
* 3.2 JDK8及以后:除了定义全局常量和抽象方法,还可以定义静态方法,默认方法
* >接口中定义的静态方法,只能通过此接口来调用
* >通过接口的实现类的对象,可以调用接口中的默认方法。如果实现类重写了默认方法,则调用的重写以后的方法
* >如果子类(实现类)继承的父类和实现的接口中声明了同名同参的方法
* 那么子类在没有重写此方法的情况下,默认调用的时父类中同名同参的方法。--->体现了类优先原则
* >如果实现类实现的多个接口中声明了同名同参的方法。
* 实现类在没有重写此方法的情况下会报错(接口冲突)。此时实现类编写重写此方法
* >在实现类中调用接口中的默认方法 的格式:接口名.super.方法名
* >在实现类中调用接口中的静态方法 的格式:接口名.方法名
* 4.接口中不能定义构造器的!意味着接口不可以实例化
* 5.Java开发中,接口通过让类去实现(implements)的方式来使用
* 若实现类覆盖了接口中的所有的抽象方法,则此实现类就可以实例化
* 若实现类没有覆盖接口中的所有的抽象方法,则此实现类只能是一个抽象类
* 6.Java类可以实现多个接口 -->弥补了Java单继承性的局限性
* 格式:class A extends b implements c,d,e{}
* 7.接口的使用:
* >接口与接口之间可以继承,而且可以多继承
* >接口的具体使用,体现多态性
* >接口,实际上可以看做是一种规范
* >接口像多态性一样有着匿名的使用
* 8.接口的应用:代理模式,工厂模式
*
* 类的内部成员之五:内部类
* 1.Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B被成为外部类
* 2.内部类的分类: 成员内部类(静态的,非静态的) vs 局部内部类(方法内,代码内,构造器内的类)
* 3.成员内部类:
* 作为外部类的成员:
* >调用外部类的结构
* >可以被static修饰
* >可以被4种不同的权限符修饰
* 作为一个类:
* >类内可以定义属性,方法名,构造器等
* >可以被final修饰,表示此类不可以被继承
* >可以被abstract修饰
* 4.内部类的使用
* 4.1 如何实例化成员内部类的对象
* ①对于非静态内部类:父类名.内部类名 对象名 = new 父类名.内部类的构造方法;
* ②对于静态内部类:先创建父类的对象:父类名 父对象名 = new 父类的构造方法();
* 然后创建内部类的对象:父类名.内部类名 对象名 = 父对象名.new 内部类的构造方法;
* 4.2 如何在成员内部类种区分调用外部类的结构
* ① 当内部类的属姓名和方法名和外部类的相同时。调用格式如下
* 调用外部类的属性和方法:外部类名.this.方法名/属性
* 调用内部类的属性和方法:this.方法名/属性
* ② 当内部类的属姓名和方法名和外部类的不相同时,直接调用即可,不用加前缀
* 4.3 开发中局部内部类的使用
* 5.注意点:当在一个方法A中定义一个内部类时,在这个内部类中再定义一个方法B。
* 如果方法A中有一个变量。当方法B中想要使用这个变量时,这个变量不能被修改
* 相当于一个常量。
* JDK 7及以前需要再这个变量前加final才能被方法B使用
* JDK 8及以后则不需要加final
* 原因:内部类的字节码文件是会和外部类平级的,在内部类中调用的外部类的方法中的变量是一个副本,副本不能被改变
*