目录
单例设计(Singleton)模式(使用static之后的一种应用)
关键字static的使用
简单理解:
static=静态的
可以用来修饰的结构
属性,方法,代码块,内部类。
理解类属性
我们可以先复习变量的分类:
按数据类型分的话可以分为基本数据类型和引用数据类型;
按类中声明的位置分的话可以分为成员变量和局部变量(方法内,方法形参,构造器外,构造器形参,代码块内)。
成员变量又可以细分为:
而static修饰变量时只能修饰成员变量:按照是否使用static修饰进行分类,使用static修饰的属性也就是(静态变量,类变量),不使用的属性称为非静态变量也就是实例变量。
静态变量和实例变量的区别
- ①个数
- 静态变量:在内存空间中只有一份,被类的多个对象所共享
- 实例变量:类的每一个实例(或对象)都保存着一份实例变量。
- ②位置
- 静态变量:jdk6及以前,存放在方法区,jdk7及之后都存放在堆中。
- 实例变量:存放在堆空间的对象实体中。
- ③加载时机
- 静态变量:随着类的加载而加载,由于类只会加载一次,所以静态变量也只有一份。
- 实例变量:随着对象的加载而加载。每个对象有一份实例变量。
- ④调用者:
- 静态变量:可以直接被类调用,也可以被对象调用。
- 实例变量:只能被对象调用。
- ⑤判断是否可以调用(从生命周期角度解释)
- ⑥消亡时间
- 静态变量:随着类的卸载而消亡
- 实例变量:随着对象的消亡而消亡
理解类方法(静态方法)
- 随着类的加载而加载
- 可通过“类.静态方法”的方式,直接调用静态方法
- 静态方法内可以调用静态属性或静态方法,不可以调用非静态结构
- static修饰的方法里面不能使用this和super关键字(如果有this,this表示实例变量,在未创建对象的时候,根本不知道this指的是谁。但是也不要觉得里面就没this了,还在只不过指的是当前类了)
单例设计(Singleton)模式(使用static之后的一种应用)
何为单例模式
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
设计的目的:
想要让类在虚拟机中只产生一个对象。
思路:
首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类的内部仍然可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法已返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向,指向类内部产生的该类对象的变量也必须定义为静态的。
我的思路:
因为创建对象只能通过构造器然后new来创建对象,所以我们将构造器私有化,外部就不能随便new来创建对象了,类的内部仍然可以使用该构造器,所以我们在类的内部创建一个对象,并通过一个静态方法来得到这个对象,(我们在外面就可以直接通过类来调这个静态方法),因为静态方法里面只能访问静态的变量,所以我们要修饰该对象为static
实现:(大体分为两种)
饿汉式:
“立即加载”,随着类的加载,当前的唯一实例就创建了
class Bank{
//该Bank类型的变量私有化,静态
private static Bank instance = new Bank;
//类的构造器私有化
private Bank(){
}
//静态方法得到唯一的Bank对象
public static Bank getBank(){
return instance;
}
…………
}
public class BankTest(){
public static void main(String[] args){
Bank bank = Bank.getBank();
}
}
懒汉式:
“延迟加载”,在需要使用的时候,进行创建。
class Bank{
//该Bank类型的变量私有化,静态,先赋值为null,调用方法的时候再创建该对象
private static Bank instance = null;
//类的构造器私有化
private Bank(){
}
//静态方法创建并得到唯一的Bank对象。
public static Bank getBank(){
if(instance == null){
instance = new Bank();
}
return instance;
}
…………
}
public class BankTest(){
public static void main(String[] args){
Bank bank = Bank.getBank();
}
}
优缺点
- 饿汉式:优:写法简单,由于内存中较早加载,使用更快,方便。是线程安全的。 缺:内存中占用时间长
- 懒汉式:缺:线程不安全。优:在需要的时候创建,节省空间。
main方法的理解
理解1:看作一个静态方法
理解2:看作一个程序的入口,格式是固定的
public static void main解析
static:总不可能先创建一个类,再拿类去调用这个main方法(你都没有入口),所以用静态修饰。
void:main作为程序入口压栈进入最底层,其他方法的返回值不做要求,因为有接受对象,但main在最低,他能返回给谁?
public:用private替代虽然不报错,但就不代表入口了,因为格式是固定的,这是他仅仅就只是一个简单的私有化方法名字叫main而已。
类的成员之四:代码块(初始块)
代码块的作用:
用来初始化类或对象的信息(即初始化类或对象的成员变量)
代码块能用的修饰符:
只能使用static进行使用(不能使用其他的任何修饰符)
分类
静态代码块:使用static修饰
- 随着类的加载而执行
- 由于类的加载只会执行一次,进而静态代码块也只会执行一次。
- 用来初始化类的信息
- 内部可以声明变量,调用属性或方法,编写输出语句等操作
- 它的执行要快于非静态代码块的执行
- 如果声明有多个静态代码块,则按照声明的先后顺序执行。非静态此处同理。
- 其内部只能调用静态结构(即静态属性和方法),不能调用非静态结构(因为类加载的时候,非静态属性方法还没“确定”)。
非静态代码块:没有使用static
- 随着对象的创建而执行
- 每创建当前类的一个实例,就会执行一次非静态代码块
- 用来初始化对象的信息
- 内部可以声明变量,调用属性或方法,编写输出语句等操作
- 其中可以调用静态结构,也可以调用非静态结构。
使用场景(自己的理解)
某些时候显示赋值静态属性时的操作不能满足赋值效果,比如所赋的值用一行语句得不到,(就比如这个值是通过某个对象的方法得到的),这显然只使用"一行"达不到想要的效果,这时候用静态代码块就很合适了。
类中属性的赋值过程
可以给类的非静态的属性(实例变量)赋值的位置有:
- ①默认初始化
- ②显示初始化/代码块中初始化(优先级一样)(放的位置先后顺序有影响,谁在前谁就先赋)
- ④构造器初始化
- *******有了对象以后*******
- ⑤对象.属性或对象.方法的方式进行赋值
字节码文件中的详细内容:
1.<init>方法在字节码文件中可以看到,每一个<init>方法都对应着一个类的构造器。(类中声明类几个构造器就会有几个<init>方法)
2.编写的代码中的构造器在编译以后就会以<init>方法的方式呈现
3.<init>方法内部的代码包含了实例变量的显式赋值、代码块中的赋值和构造器中的代码。
4.<init>方法用来初始化当前创建对象的信息的。
给的实例变量赋值位置这么多,开发中怎么选择?
- 显式赋值:适合于每个对象的属性值相同的场景
- 构造器中赋值:比较适合于每个对象的属性值不相同的场景
过程判断技巧:
加载整个过程:由父及子,静态先行。构造器之前一定先有非静态的代码块执行。
final关键字的使用
理解:
final = 最终的
可以修饰的结构:
类:
final修饰类表示此类不能被继承。比如:Java里定义的String类就是用final修饰的。
什么时候用:功能不必要在扩展,已经完善!
final class A{
}
//此时就会报错,不能被继承
class B extends A{
}
方法:
final修饰方法表示此方法不能被重写。比如:Object类中的getClass()
class C{
public final void method(){
}
}
class D extends C{
//这里就会报错,不能被重写
public void method(){
}
}
变量:
总结:既可以修饰成员变量,也可以修饰局部变量。此时的"变量"就变成了常量,意味着一旦赋值就不会被更改。
修饰成员变量:
哪些位置可以赋值:显式赋值、代码块中赋值、构造器中赋值
修饰局部变量:
方法内声明的局部变量,方法的形参:调用局部变量前就必须赋值,且不能被更改
注意:final修饰的变量,就没有默认值一说了
全局常量:
开发中经常使用static和final搭配使用修饰成员变量。兼具共同特点,称此成员变量为全局变量。比如Math.PI。
抽象类(abstract )(面向对象特征之四:抽象性)
理解:
abstract = 抽象的
修饰的结构:
类:
- 此类称为抽象类
- 抽象类不能实例化
- 抽象类中是包含构造器的,因为子类对象实例化时需要直接或间接地调用父类的构造器。
- 抽象类中可以没有抽象方法,但是有抽象方法的类一定是抽象类
方法:
- 此方法为抽象方法
- 抽象方法只有声明,没有方法体(大括号也不能要)
- 抽象方法其功能是确定的,通过方法的声明即可确定,只是不知道如何具体实现而已
- 子类必须重写所有抽象方法之后,方可实例化,否则其子类仍然是一个抽象类
abstract class Person{
//抽象类里的属性和构造器不做要求
String name;
int age;
public Person(){}
//看名字就知道是什么作用,但因为abstract修饰,不能有方法体包括大括号
public abstract eat();
public abstract sleep();
}
//第一种:必须重写所有抽象方法,否则就必须声明为抽象类(第二种)
class Student extends Person{
public abstract eat(){
System.out.println("学生吃的很营养");
}
public abstract sleep(){
System.out.println("学生睡很长时间");
}
}
//第二种:只能声明为abstract
abstract class Student extends Person{
public abstract eat(){
System.out.println("学生吃的很营养");
}
//没有重写sleep。
}
abstract不能使用的场景
1.不能修饰属性,构造器,代码块
2.(自洽)不能与哪些关键字一起使用
*不能修饰私有方法。(private)私有方法不能被重写
*不能修饰静态方法。(static)避免静态、抽象方法被类调用
*不能修饰final方法。(final)final修饰的方法不能被重写
*不能修饰final类。(inal)final修饰的类不能被继承
理解:(final和abstract水火不容,完全相反)
接口(interface)
接口的本质是契约、标准、规范,就像我们的法律一样。制作好以后大家要遵守。接口是"has-a"的关系
定义接口的关键字:interface
接口内部结构的说明:
可以声明:
属性:
必须使用public static final修饰(就算不写,他也会默认给你加上)
方法:
JDK8之前:只能声明抽象方法,修饰为public abstract(也可以省略public abstract,会默认给你加上)
JDK8中:声明静态方法、默认方法
public interface CompareA{
//JDK8:静态方法
public static void method1(){
System.out.println("我爱玛俐!");
}
//JDK8:默认方法
public default void method2(){
System.out.println("我爱莎莉娜!");
}
}
public class SubClass implements CompareA{
//可以重写默认方法
}
public class SubClass implements CompareA{
//可以重写默认方法
}
public class SubClassTest{
public static void main(String[] args){
//接口中声明的静态方法只能由接口去调用,不能用其实现类调用
CompareA.method1();
//SubClass.method1();
//接口中声明的默认方法可以被实现类继承,实现类在没有重写此方法的情况下,默认调用接口中的静态方法;如果重写了,就调用重写的方法。
SubClass s1 = new SubClass();
s1.method2();
}
}
1.接口中可以声明静态方法,只能拿接口去调用该方法,不能拿实现它的类去调用
2. 默认方法:default声明的默认方法可以被实现类继承,实现类中如果没重写该方法,调用的就是默认的,重写后调用重写的
3.类实现了两个接口,而两个接口定义了同名同参数的默认方法,则实现类在没有重写该方法的情况下会报错。
4.类优先原则:子类继承了父类并实现了接口、父类和接口中声明了同名同参数的方法,(且接口中的该方法是默认方法),子类在没有重写该方法的情况下,调用的是父类中的方法。
JDK9:声明私有方法:不重要
不可以声明:
不可以声明代码块,构造器。
格式:
class A extends SuperA implements B,C{}{
//重写抽象方法的操作…………
}
//A相较于SuperA来讲,叫子类
//A相较于B,C来讲,叫做实现类
说明 :(以上述例子为例)
1.类可以实现多个接口
2.类针对于接口的多实现,一定程度上就弥补了类的单继承的局限性
3.类必须将实现的接口中的所有的抽象方法都重写(实现),方可实例化,否则此实现类必须声明为抽象类。
关系汇总
接口与类的关系:实现关系
接口与接口的关系:继承关系,且可以多继承
interface AA{
void method1();
}
interface BB{
void method2();
}
interface CC extends AA,BB{//接口可以多继承
}
接口的多态性:
先回忆一下类的多态性:父类 变量名 = new 子类对象
接口的多态性:接口名 变量名 =new 实现类对象;(其他与类的多态性一样)
抽象类与接口的区别
共性:都可以声明抽象方法,都不能实例化。
不同:
- 抽象类一定有构造器,接口没有构造器
- 类与类之间是继承关系,类与接口之间是实现关系,接口与接口之间是多继承关系。
NO | 区别点 | 抽象类 | 接口 |
---|---|---|---|
1 | 定义 | 可以包含抽象方法的类 | 主要是抽象方法和全局常量的集合 |
2 | 组成 | 构造方法、抽象方法、普通方法、常量、变量 | 抽象方法、(idk8.0:默认方法、静态方法) 常量 |
3 | 使用 | 子类继承抽象类(extends) | 子类实现接口(implements) |
4 | 关系 | 抽象类可以实现多个接口 | 接口不能继承抽象类,但允许继承多个接口 |
5 | 常见设计模式 | 模板方法 | 简单工厂、工厂方法、代理模式 |
6 | 对象 | 都通过对象的多态性产生实例化对象 | 都通过对象的多态性产生实例化对象 |
7 | 局限 | 抽象类有单继承的局限 | 接口没有此局限 |
8 | 实际 | 作为一个模板 | 是作为一个标准或是表示一种能力 |
类的成员之五:内部类(我是只听懂一点)
理解就行,要求不高
什么是内部类?
将一个类A定义在另一个类B里,里面的类A就是内部类,类B就是外部类
为什么使用内部类?
具体来说,当一个事物A的内部,还有一个部分需要一个完整的结构B进行描述,而这个内部的完整的结构B又只为外部事物A提供服务,不在其他地方单独使用,那么整个内部的完整结构B最好使用内部类。
总的来说:遵循高内聚、低耦合的面向对象的开发原则
举例
HashMap类中声明的Node类,Thread类中声明的State类
分类
成员内部类:直接声明在外部类的里面
使用static修饰的:静态成员内部类
不使用:非静态成员内部类
局部内部类:声明在方法内,构造器内,代码块内
主要知识
成员内部类的理解(理解)
- 从类的角度看:
- 可以声明属性、方法、构造器、代码块、内部类等结构
- 此内部类可以声明父类,可以实现接口
- 可以使用final、abstract修饰
- 从外部类的成员角度看:
- 在内部可以调用外部类的结构,比如:属性或方法
- 能使用全部的权限修饰符修饰
- 可以使用static修饰
如何创建成员内部类的实例(理解)
如何在成员内部类中调用外部类的结构 (了解)
外部类也有一个name属性
局部内部类的基本使用(熟悉)
这里用到了Compareble接口,后面也会复习
枚举类
理解:
枚举类型本质上也是一种类,只不过是这个类的对象是有限的,固定的几个,不能让用户修改
生中的例子:星期类:对象有限,从1到7;性别类:男和女,对象有限
开发建议
如果针对于某个类,其实例是确定个数的,则推荐将此类声明为枚举类
如果枚举类的实例只有一个,可以看作是单例的实现方式
如何自定义枚举类
JDK5.0之前
class Season{
//声明当前类的对象的实例变量private final
private final String seasonName;//季节名称
private final String seasonDesc;//季节描述
//1.私有化类的构造器private
private Season(String seasonName,String seasonDesc){
this.seasonDesc = seasonDesc;
this.seasonName = seasonName;
}
//提供get方法
public String getSeasonName(){
return SeasonName;
}
public String getSeasonDesc(){
return SeasonDesc;
}
//4.创建当前类的实例public static final
public static final Season spring = new Season("春天","春暖花开");
public static final Season summer = new Season("夏天","夏日炎炎");
public static final Season autumn = new Season("秋天","秋高气爽");
public static final Season winter = new Season("冬天","白雪皑皑");
}
JFK5.0之后enum定义枚举类
主要区别是创建类的实例的时候
enum Season{
//必须在枚举类的开头声明多个对象,多个对象之间用逗号隔开
//创建当前类的实例public static final……一样的可以去掉
spring("春天","春暖花开"),
summer("夏天","夏日炎炎"),
autumn("秋天","秋高气爽"),
winter("冬天","白雪皑皑");
//2.声明当前类的对象的实例变量private final
private final String seasonName;//季节名称
private final String seasonDesc;//季节描述
//3.私有化类的构造器private
private Season(String seasonName,String seasonDesc){
this.seasonDesc = seasonDesc;
this.seasonName = seasonName;
}
//4.提供get方法
public String getSeasonName(){
return SeasonName;
}
public String getSeasonDesc(){
return SeasonDesc;
}
}
Enum中的常用方法
注意点
- 使用enum关键字定义的类,默认其父类是java.long.Enum类
- 不能再显示的定义其父类,否则会报错
熟悉其中的常用方法
- toString():默认打印这个实例对象的名字(所以就不用重写Object里的toString方法了)
- values( ):获取这个枚举类里的所有实例对象(重要)
- valueOf(String objName):根据传进来的字符串返回枚举类中对应的实例对象,如果没有则报错(重要)
- ordinal( ):返回对应的下标(第几个声明的,了解)
枚举类实现接口操作
- 1.枚举类实现接口,在枚举类中重写接口中的抽象方法,当通过不同的枚举类中的对象来调用该方法时,结果是一样的
- 2.让枚举类的每一个对象重写接口中的抽象方法,执行时,结果是不同的。
Annotation(@)(不要当作重点,现在)
不用深究,学框架再了解,包括自定义注解,现在不用学
修饰结构
像修饰符一样,能修修饰很多结构
基本三注解:(了解)
- @Override: 只能用来修饰方法,限定重写父类的方法。
- @Deprecated :用于表示所修饰的元素(类、方法等)已过时。通常因为所修饰的结构危险或存在更好的选择
- @SuppressWarnings:抑制编译器警告
元注解:对现有的注解进行注解的注解
- 暂时提4个
- @Target:能修饰的结构类型
- @RetentionPolicy :生命周期
- @Documented : 表明这个注释应该被Javadoc工具记录
- @Inherited :允许此注解可被继承
框架=注解+反射+设计模式
包装类
为什么使用包装类?
为了使得基本数据类型的变量具备有引用数据类型变量的相关特征(比如封装性,多态性,继承性),我们给各个基本数据类型的变量都提供了对应的包装类。
8种包装类
- byte—>Byte
- short—>Short
- int—>Integer
- long—>Long
- float—>Float
- double—>Double
- 上面的父类都为Number类**
- char—>Character
- boolean—>Boolean
掌握基本数据类型与包装类之间的转换
为什么需要转换?
- 一方面在有些场景下,需要使用基本数据类型对应的包装类的对象,此时就需要将基本数据类型的变量转换为包装类的对象。比如ArrayList的add(Object ob)
- 对于包装类来说,既然我们使用的是对象,对象是不能进行加减乘除等运算的。为了能进行这些运算就需要将包装类的对象转换为基本数据类型变量
自动装箱和拆箱(新特性)(灰常重要)
- 直接就可以赋值
老版本的操作(不重要)
- 基本转引用(老版本)
- 使用包装类的构造器
- 调用包装类的valueOf( )
- 引用转基本(老版本)
- 调用包装类的xxxvalue()
- 注意点,基本变引用之后,对于成员变量来说其默认值变化了
String 与 基本数据类型、包装类之间的转换
- 基本引用转String:
- 调用String的重载的静态方法String.valueOf(xxx);
- 基本数据类型的变量+"";
- String转基本引用
- 调用包装类的静态方法parseXxx();