Java第二阶段
1.类变量和类方法
类变量
(静态变量/静态属性):能够被同类的对象共享的一块内存空间,用static修饰。
**语法:**访问修饰符 static int count;(推荐),注意:访问要遵循访问权限。
static 访问修饰符 int count;
访问:类名.类变量(推荐)或对象名.类变量
注:因为类变量随着类的加载而创建,所以即使没有对象实例也可以访问。
使用细节:
1.什么时候用类变量:当需要让某个类的所有对象共享一个变量时。
2.类变量和普通属性的区别:类变量共享,而实例变量是独享的。
3.类变量的生命周期是随着类的加载而开始,随着类的销毁而销毁。
类变量内存剖析:
说法1:堆。说法2:方法区的静态域。
解释:在jdk8之前在方法区,jdk8以及之后在堆中,通过反射机制,是这个类的class对象。
类方法
(静态方法):可以类比类变量进行记忆。
**语法:**普通方法里加个static。
调用:类名.方法名(推荐)或对象名.方法名
使用细节:
1.什么时候使用类方法:当我们希望不创建对象实例就可以直接调用该方法时(即当做工具来使用),就非常适合。
2.类方法和普通方法的生命周期一样,都是随着类的加载而加载,将结构信息存储在方法区。注:类方法中无this参数。普通方法中可以有。
3.类方法中不允许使用和对象有关的关键字,如this,super。成员方法可以。
4.重要:类方法中只能访问(静态成员)静态属性和静态方法。而非静态方法可以访问(所有的成员)静态成员和非静态成员。
面向对象进阶
深入理解main方法
形式:public static void main(String[] args) {…}
1.main方法是java虚拟机来调用的。
2.为什么是public?因为这样java虚拟机才能调用,访问权限必须是public。
3.为什么是static?java虚拟机在执行main方法时不需要创建对象。
4.void:不需要返回值。
5.String[] args:main方法接收String类型的数组参数,这个数组保存了java运行时传递给该类的参数。参数是什么时候传进去的?在执行的时候。如:java 运行的类名 参数1 参数2 参数3
main方法特别提示:
在main方法中,可以直接访问main方法所在类的静态成员,但不能直接访问非静态成员,如果需要访问,必须创建对象实例,然后去调用非静态成员。如:我们在main方法中经常 new一个对象。
如何在idea中传递参数给java程序:
先找到右上角程序运行键的edit
代码块
定义:又称初始化块,属于类中的成员,可以理解成它是只有方法体的方法。而且不用通过类名或者对象名来显式调用,而是在加载类或者创建对象时被隐式调用。
语法: 【修饰符】{。。。};//分号可写可省略
注意:修饰符可选,但只能选static。有static叫静态代码块,没有叫普通代码块
代码块使用细节:
1、静态代码块在类加载时被调用,并且只能调用一次。而普通代码块在创建一个对象时被调用,每new一个对象就调用一次。
3、类什么时候被加载:(1)创建对象时(2)创建子类实例时,父类被加载,而且是先被加载(3)使用类的静态成员时(静态属性、静态方法)
4、如果只是(3)使用类的静态成员时,普通代码块不会被执行。
5、创建一个对象时,在一个类中的调用顺序:(1)调用静态代码块(静态属性和静态代码块,谁写在前面谁先执行;(2)再调用普通代码块(普通属性和普通代码块谁在前谁先执行);(3)最后调用构造器
6、5的原因:构造器的最前面隐含了super()和调用普通代码块,而静态代码块在类加载的时候就执行完毕了,因此优先于普通代码块。
class A{
public A(){//构造器
//这里有隐藏的执行要求
/(1)super()://这个知识点,在前面讲解继承的时候,老师说过
//(2)调用普通代码块的
System.out.println("ok");
}
}
7、当创建一个子类对象时(有继承关系),他们的静态成员(静态代码块、静态属性)和普通成员(普通代码块、普通属性)、构造器的调用顺序:
(1)父类静态代码块和静态属性
(2)子类静态代码块和静态属性
(3)父类普通代码块和普通属性初始化
(4)父类构造器
(5)子类普通代码块和普通属性
(6)子类构造器
代码块练习:
代码块的使用场景:
**23种设计模式:**它是程序员在大量实践中总结和理论之后优选的代码结构、编程风格、以及解决问题的思考方式。不同的情况就使用不同的设计模式,减去了程序员思考和摸索的时间。
单例模式中的饿汉式:
三步走:
1、构造器私有化(防止类的外部直接创建对象)
2、类的内部创建对象(外部不能创建,于是在类的内部创建,用private,static修饰,private让外部不能直接访问,static是因为下一步暴露的方法是静态的,而静态方法只能访问静态属性)
3、向外部暴露一个公共方法getInstance(用static修饰,因为外部要想不创建对象就能调用方法,方法必须是static修饰)
class SingleTon01{
private SingleTon01(){};
private static SingleTone1 instance = new singleTone1();
public static SingleTon01 getInstance(){
return instance;
}
}
饿汉式缺点:对象创建了但是没用,造成了资源的浪费。
懒汉式:特点:在饿汉式创建内部对象时不直接创建,只声明。需要时,在getInstance中再创建。
class Text8 {
private String name;
private Text8(String name) {
this.name = name;
}
private static Text8 text8;
public static Text8 getInstance() {
if(text8 == null){
text8 = new Text8("xiaogu");
}
return text8;
}
@Override
public String toString() {
return "Text8{" +
"name='" + name + '\'' +
'}';
}
}
●饿汉式VS懒汉式
1.二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。
2.饿汉式不存在线程安全问题,懒汉式存在线程安全问题。(后面学习线程后,会完善一把)
3.饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题。
4.在我们javaSE标准类中,java.lang.Runtime就是经典的单例模式。
final关键字
**final:**意思是最终,它可以修饰类,属性和方法。
当不希望类被继承,可以用final修饰。
当不希望父类的某个方法被子类覆盖或重写,可以用final修饰。
当不希望类的某个属性的值被修改,可以用fianl修饰。
当不希望某个局部变量被修改,可以用final修饰。
final使用细节:
1、final修饰基本数据类型,变量的值不能修改;
修饰引用数据类型,地址值不能修改,内部的属性值可以修改。
共同点:数据不能被修改。(黑马知识点)
2、final修饰的属性又叫常量,一般用XX_XX_XX命名,所有字母大写。
3、final修饰的属性在定义时必须赋初值,并且以后不能被修改。定义的位置有三个:(1)声明时:public final double TAX_RATE = 0.08(2)在构造器中(3)在代码块中
4、如果final修饰的属性是静态的,那么只能在(1)声明时,(2)静态代码块中进行初始化赋值,构造器和普通代码块不行,因为静态属性的初始化在类加载时就完成了,不需要创建对象。
5、用final修饰的类不能被继承,但可以实例化对象。
6、如果类不是final类,但是包含final方法,类可以被继承,但方法不能被重写。
7、如果一个类已经是final类,就没必要把它的方法变成final方法,因为该类已经不能被继承了,方法也没有了重写的必要。
8、final不能修饰构造器。
9、final和static往往搭配使用,效率更高。举例说明:如果我只是想使用一个类变量(static修饰),会同时导致类的加载,而加上final,就不会导致类的加载,运行效率更高,这是因为底层编译器做了优化。
10、包装类(Integer、Double、Float、Boolean等都是fianl类),String也是final类。
抽象类
引出:当父类的某些方法需要声明,但又不确定如何实现时,可以将其声明为抽象方法,该类为抽象类。该抽象方法让子类来实现。
抽象类的介绍
1、用abstract修饰一个类,该类为抽象类。如:public abstract 类名{}
2、用abstract来修饰一个方法,该方法为抽象方法。如:public abstract 返回类型 方法名(参数列表);//没有方法体
3、抽象类的更多价值在于设计,是设计者设计好后,让子类继承并实现抽象类。
4、是考官比较爱问的知识点,在框架和设计模式使用较多。
抽象类使用细节:
1、抽象类不能被实例化
2、抽象类不一定包含抽象方法
3、一旦类包含了抽象方法,则该类必须是抽象类,用abstract修饰。
4、abstract只能修饰类和方法,不能修饰属性和其他的。
5、抽象类本质还是类,只是被abstract修饰了,所以可以有类的各种成员
6、如果一个类继承了抽象类,则它要么也声明成抽象类,要么实现父类的所有抽象方法。所谓实现方法,指的是最少有方法体。
7、抽象方法不能使用private、final和static来修饰,因为这些关键字和方法重写是相违背的。说明:用了private修饰,子类就不能重写,用了final修饰,表示该方法不能被覆盖或重写,用了static修饰,该方法不需要创建对象就可以被调用,而abstract修饰的方法连方法体都没有,根本无法实现方法。
练习:抽象类最佳实践——模版设计模式(23种设计模式中的一种)
设计一个抽象类(Template),能完成如下功能:
1)编写方法calculateTime(),可以计算某段代码的耗时时间
2)编写抽象方法job()
3)编写一个子类Sub,继承抽象类Template,并实现job方法。
4)编写一个测试类TestTemplate,看看是否好用
abstract class Template{//抽象类
public abstract void job();//抽象方法
public void caleTimes(){//统计耗时多久是确定
//统计当前时间距离1970-1-10:0:0的时间差,单位ms
long start=System.currentTimeMillis();
job();
long end = System.currentTimeMillis();
System.out.println("耗时:“+(end-start));
接口
接口介绍:接口就是给出一些没有实现的方法,封装到一起,当某个类要使用的时候,根据具体情况把这些方法写出来。接口是java单继承的一种补充机制。
语法:
interface 接口名{
//属性
//方法
}
class 类名 implements 接口{
自己的属性
自己的方法
必须实现的接口的抽象方法,//或者把类变成抽象类,则不需要实现抽象方法
}
注意:在jdk7前,接口里的所有方法都没有方法体,jdk8后接口类可以有静态方法,默认方法。(普通方法)
什么时候使用接口?(接口的应用场景)
如:现在有一个项目经理,管理三个程序员,功能开发一个软件,为了控制和管理软件,项目经理可以定义一些接口,然后由程序员具体实现。
实际要求:3个程序员,编写三个类,分别完成对Mysql,Oracle,DB2数据库
的连接connect,close…
实现:在接口里定义数据库的连接方法connect和关闭方法close,让程序员1去实现具体的mysql的connect方法和close方法,让程序员2去实现具体的Oracle的connect方法和close方法。
接口的使用细节
1、接口不能实例化
2、接口中的所有方法都是public方法,接口中的抽象方法可以不用abstract修饰,因为编译器默认是abstract。
3、一个普通类实现接口,必须把接口中的所有抽象方法都实现。
4、抽象类实现接口,可以不用实现接口的方法。
5、一个类可以同时实现多个接口。如:A implements B,C{……},并且A同时拥有了BC的属性,和继承一样。
6、接口中的属性,是默认 public static fianl 修饰的
7、接口中属性的访问形式:接口名.属性名
8、一个接口不能继承其他的类,但是可以继承多个别的接口。如:interface A extends B,C{…}
9、接口的修饰符只能是public和默认,这点和类的修饰符一样。
接口 VS 继承
1、接口和继承解决的问题不同
继承的价值主要在于解决代码的复用性和可维护性
接口的价值主要在于设计,设计好各种规范(方法),让其他类去实现这些方法。
2、接口比继承更加灵活
继承满足 is -a的关系,而接口只要满足 like -a 的关系
3、接口在一定程度上实现代码解耦
接口的多态特性
1、多态参数:举例:Usb接口,Usb usb,既可以接收手机对象,又可以接收相机对象,就体现了接口的多态。(接口的引用可以指向实现了接口的类的对象)
2、多态数组
package com.text.interface1;
public class Text1 {
public static void main(String[] args) {
Usb[] usbs = new Usb[2];
usbs[0] = new Camera();
usbs[1] = new Phone();
for (int i = 0; i < usbs.length; i++) {
usbs[i].worl();
if(usbs[i] instanceof Phone){
((Phone) usbs[i]).call();
}
}
}
}
interface Usb {
public void work();
}
class Phone implements Usb{
public void call(){
System.out.println("call...");
}
@Override
public void work() {
System.out.println("phone里的work");
}
}
class Camera implements Usb{
@Override
public void work() {
System.out.println("camera里的方法");
}
}
3、多态传递,如果IG继承(extends)了IH接口,而Teacher类实现(implements)了IG接口,那么,实际上就相当于Teacher类也实现了IH接口,也需要实现IH接口的抽象方法,也可以拿到IH接口的变量。
总结:类的五大成员:1、属性2、方法3、构造器4、代码块5、内部类
四种内部类
内部类介绍:一个类的内部又嵌套了另一个类,被嵌套的类就叫做内部类(inner class),嵌套其他类的类就做外部类(outer class),和外部类地位相同的类叫做外部其他类。
语法:
class Outer{//外部类
class Inner{
//内部类
}
}
class Other{//外部其他类
内部类的分类
1、定义在外部类的局部位置上(通常在方法内或代码块):
(1)局部内部类(有类名)
(2)匿名内部类(没有类名,重点!!!!java里非常常见)
2、定义在外部类的成员位置上
(1)成员内部类(没有static修饰)
(2)静态内部类(使用static修饰)
局部内部类
使用细节:
1、可以直接访问外部类的所有成员,包括私有成员
2、该类不能添加访问修饰符,因为它只算一个局部变量,局部变量是不能使用修饰符的。但是可以用final修饰,因为局部变量可以使用final。
3、作用域:仅仅在定义内部类的方法或代码块中
4、局部内部类如何访问外部类的成员?直接访问
5、外部类如何访问局部内部类的成员?创建内部类对象,再访问(注:必须在作用域内)
6、外部其他类不能访问局部内部类,因为局部内部类地位是一个局部变量
7、如果外部类和局部内部类的成员重名时,默认遵循就近原则;
如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。外部类名.this
是一个特殊语法,表示当前外部类的实例对象
通过 外部类名.this.成员
,可以明确指定:
- 访问的是当前外部类对象的成员(而非局部内部类的成员)。
- 这种写法是 Java 的固定语法规则,用于解决作用域冲突
匿名内部类
语法:
new 类或接口(参数列表){
类体
};
匿名(Anonymous)内部类的本质:
举例1:基于接口的匿名内部类
需求:使用IA接口,并创建tiger对象。
传统方式:直接创建一个tiger类,实现接口。但是如果我只会使用一次tiger类?却要创建一个tiger类就太麻烦了,因此引出了匿名内部类的使用来简化开发。
匿名内部类的方式:不需要创建tiger或者cat类,直接创建一个匿名内部类来实例化对象,最后返回给tiger。
package com.text.interface1;
public class Text3 {
public static void main(String[] args) {
Outer outer = new Outer();
outer.f1();
}
}
class Outer{
public void f1(){
IA tiger= new IA(){//创建匿名内部类,jdk底层会创建匿名内部类0uter04$1(jsk底层分配的名字),立即马上就在堆中创建了0uter04$1实例,并且把地址返回给tiger。并且匿名内部类只使用一次就不能再使用了。
@Override
public void cry() {
System.out.println("老虎叫唤、、、");
}
};
tiger.cry();
}
}
interface IA{
public void cry();
}
getClass()方法就是获取对象的运行类型,看底层可以发现匿名内部类是系统分配了名字的,只是没有显示出来。
举例2:基于类的匿名内部类
package com.text.interface1;
public class Text4 {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.father.text();
}
}
class Father{
String name ;
public Father(String name) {
this.name = name;
}
public void text(){
System.out.println("father");
};
}
class Outer02{
Father father = new Father("jack"){//基于类的匿名内部类
@Override
public void text() {
System.out.println("我是匿名内部类");
}
};
}
说明:
1、father编译类型:Father,运行类型:匿名内部类Outer04$02(底层创建的),使用getClass()方法可以看到,02是因为前面有了01.
2、匿名内部类的方法体内部可以重写Father类的方法。因为它的底层是继承了Father类。
3、jack是传递给Father的构造器的。
举例3:基于抽象类的匿名内部类,和基于类的情况基本一致,只是可能需要重写抽象类的方法。
匿名内部类使用细节:
1、匿名内部类本身是一个类,因此具有类的特征,同时它也是一个对象,可以直接调用方法。
new A(){
@override
public void cry(){
System.out.println("hello~");
}.cry();
或者
A a = new A(){
@override
public void cry(){
System.out.println("hello~");
};
a.cry();
2、可以直接访问外部类的所有成员,包括私有成员
3、不能添加访问修饰符,因为它的地位就是局部变量
4、作用域:仅仅在定义匿名内部类的方法或代码块中
5、匿名内部类访问外部类成员:直接访问
6、外部其他类不能访问匿名内部类
7、如果外部类和内部类的成员重名,遵循就近原则,如果想访问外部类的成员,可以使用(外部类名.this.成员)去访问。
匿名内部类的最佳实践
1、当做实参直接传递,简洁高效
public class Text5 {
public static void main(String[] args) {
say(new SA() {
@Override
public void hi() {
}
});//这里的匿名内部类就是一个实参传给say方法。
}
public static void say(SA sa){
System.out.println("hello————");
}
}
interface SA{
public void hi();
}
输出:hello————
对比传统方法:传统方法需要先创建一个类,再实例化对象,然后调用say方法,因此使用匿名内部类可以简化开发。
练习2:
package com.text.interface1;
public class Text6 {
public static void main(String[] args) {
new Cellphone().alarmclock(new Bell() {
@Override
public void ring() {
System.out.println("懒猪起床了————");
}
});
new Cellphone().alarmclock(new Bell() {
@Override
public void ring() {
System.out.println("小伙伴们上课啦");
}
});
}
}
interface Bell{
public void ring();
}
class Cellphone{
public void alarmclock(Bell bell) {
bell.ring();
}
}
匿名内部类涉及:(1)继承(2)多态(3)动态绑定机制(4)内部类
成员内部类
说明:定义在类的成员变量的位置上,没有static修饰。
使用细节:
1、可以直接访问外部类的所有成员,包括私有的
2、可以添加任意的访问修饰符(public、protected、默认、private),因为它的地位是类的成员。
3、作用域是整个外部类类体
4、成员内部类访问外部类成员:直接访问
5、外部类访问成员内部类:创建成员内部类对象,再访问
6、外部其他类访问成员内部类:两种方式:(1)创建外部类实例,外部类实例去创建内部类对象,就可以访问了(2)在外部类编写一个方法,返回内部类的一个实例,如:return new inner01;
7、如果外部类和内部类成员重名了。内部类访问的话,遵循就近原则, 如果想要访问外部类成员,可以使用(外部类名.this.成员)访问。
静态内部类
使用细节:
1、可以访问外部类的所有静态成员,包括私有的,但不能访问非私有的成员。
2、可以添加任意的访问修饰符(public、protected、默认、private),因为它的地位是类的成员。
3、作用域是整个外部类类体
4、静态内部类访问外部类成员:直接访问静态成员
5、外部类访问静态类:创建对象再访问
6、外部其他类访问静态内部类: 两种方式:(1)通过类名直接访问,因为是静态的(前提是满足访问权限)(2)在外部类编写一个方法,可以返回内部类的对象实例,该方法是静态还是非静态都可以。
7、如果外部类和内部类成员重名了。内部类访问的话,遵循就近原则, 如果想要访问外部类成员,可以使用(外部类名.成员)访问。
注解(Annotation)
说明:
1、注解也称元数据,用于修饰和解释包、类、方法、属性、构造器、局部变量等数据信息
2、和注释一样,不影响程序逻辑,但注解可以被编译或运行,相当于嵌入代码中的补充信息
3、在javaSE中,注解的目的比较简单,例如标记过时的功能,忽略警告等。在javaEE中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替javaEE旧版中遗留的繁冗代码和XML配置
@Override
使用说明:用于限定某个方法,必须重写了父类的方法,否则编译不通过。并且该注解只能用于方法。(该注解可以没有,但有的话必须是被重写的方法)。
查看**@Override**源码:
@Target(ElementType.METHOD)//说明只能用于修饰方法
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
补充:@interface不是接口,是一个注解类,jdk1.5后才有。@Target是修饰注解的注解,称为元注解。
@Deprecated
1.用于表示某个程序元素(类、方法)已过时。表示不推荐使用,但仍然可以使用。
2.源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})//@Deprecated可以修饰方法,包,字段,参数等等
public @interface Deprecated {
}
3.deprecated的作用可以做到新旧版本的兼容和过渡。
@SuppressWarnings
说明:抑制编译器警告
说明各种值
1)unchecked是忽略没有检查的警告
2)rawtypes是忽略没有指定泛型的警告(传参时没有指定泛型的警告错误)
3)unused是忽略没有使用某个变量的警告错误
4)@SuppressWarnings可以修饰的程序元素为,查看@Target
5)生成@SupperssWarnings时,不用背,直接点击左侧的黄色提示,就可以选择(注意可以指定生成的位置)
源码:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})//可用于类型,字段,方法,构造器...
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();//说明该类可以传参数
}
四种元注解
1、Retention:只能用于修饰一个注解的定义,用于指定该注解可以保留多长时间。
有三种情况:
@Retentionl的三种值
1)RetentionPolicy.SOURCE:只在编译时生效,编译器使用后,直接丢弃这种策略的注释
2)RetentionPolicy.CLASS:在class文件中仍然生效,编译器将把注解记录在class文件中.当运行Java程序时,JVM不会保留注解。这是默认值
3)RetentionPolicy.RUNTIME:在运行时仍然生效,编译器将把注解记录在class文件中.当运行Java程序时,JVM会保留注解,程序可以通过反射获取该注解。
理解:三种情况是层层递进的关系,@Override就是第一种情况。
2、Target:用于指定注解可以在哪些程序元素上使用。
3、Documented:用于指定被该注解类修饰的注解会被javadoc工具提取成文档。如@Deprecated。
4、@Inherited:被它修饰过的注解具有继承性,即如果某个类使用了被@Inherited修饰过的注解,则它的子类将自动具有该注解。
OD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})//可用于类型,字段,方法,构造器…
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();//说明该类可以传参数
}
##### 四种元注解
1、Retention:只能用于修饰一个注解的定义,用于指定该注解可以保留多长时间。
有三种情况:
>@Retentionl的三种值
>1)RetentionPolicy.SOURCE:只在编译时生效,编译器使用后,直接丢弃这种策略的注释
>2)RetentionPolicy.CLASS:在class文件中仍然生效,编译器将把注解记录在class文件中.当运行Java程序时,JVM不会保留注解。这是默认值
>3)RetentionPolicy.RUNTIME:在运行时仍然生效,编译器将把注解记录在class文件中.当运行Java程序时,JVM会保留注解,程序可以通过反射获取该注解。
理解:三种情况是层层递进的关系,@Override就是第一种情况。
2、Target:用于指定注解可以在哪些程序元素上使用。
3、Documented:用于指定被该注解类修饰的注解会被javadoc工具提取成文档。如@Deprecated。
4、@Inherited:被它修饰过的注解具有继承性,即如果某个类使用了被@Inherited修饰过的注解,则它的子类将自动具有该注解。