JDK | JRE | JVM
JDK(Java SE Development Kit),Java标准开发包,它提供了编译、运行Java程序所需的各种工具和资源,包括Java编译器、Java运行时环境(JRE),以及常用的Java类库等。
JRE( Java Runtime Environment) ,
Java运行环境,用于解释执行Java的字节码文件。普通用户而只需要安装 JRE(Java Runtime Environment)来运行 Java 程序。而程序开发者必须安装JDK来编译、调试程序。
JVM(Java Virtual Mechinal),Java虚拟机,是JRE的一部分。它是整个java实现跨平台的最核心的部分,负责解释执行字节码文件,是可运行java字节码文件的虚拟计算机。所有平台的上的JVM向编译器提供相同的接口,而编译器只需要面向虚拟机,生成虚拟机能识别的代码,然后由虚拟机来解释执行。
java数据类型
面向过程 | 面向对象区别
面向过程 | 面向对象 | |
---|---|---|
区别 | 分析出解决问题所需要的步骤,然后用函数实现一个一个步骤,使用的时候按解决问题的流程依次调用 | 将问题中涉及到的实体抽象成一个个类,将与这个实体有关的数据和行为封装在类中,通过调用这些实体的属性和方法去解决问题。 |
优缺点 | 性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源,当问题比较简单时,使用面向过程编程,程序流程就比较简洁清楚,但当程序很大时,面向过程的流程就会变得十分复杂,且不方便维护和扩展。 | 更贴近现实世界的思想,模块化编程使得程序容易维护,扩展。 |
OOP三大特征
- 1)封装(Encapsulation) :
**封装:**是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
封装原则:
将不需要对外提供的内容都隐藏起来。
把属性都私有化,提供getter/setter方法 ,在getter/setter中加入属性控制语句
优点:通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。
缺点:不能直接调用属性,增加了代码的冗余
- 2)继承(inheritance):
继承是从已有的类中派生出新的类, 新的类能拥有已有类的数据属性和行为,并能扩展新的能力。
优点
减少了代码的冗余,提高了代码的复用性;便于功能扩展;实现多态的前提
缺点
父类变化子类也会跟着变化,使得类与类之间的具有一定程度上的强耦合
- 3)多态
多态首先是建立在继承的基础上的。不同的子类在继承父类后分别都重写覆盖了父类的方法,使得父类同一个方法,在继承的子类中表现出不同的形式。
父类的引用指向了子类对象时,调用同名的方法实际上调用的是子类重写过的方法。即运行的时候再去确定实际使用的对象类型是什么,进而确定实际上调用的方法是哪个。
实现:
1) 有子类和父类,有继承或实现; 2) 子类必须重写父类方法; 3) 父类引用或接口指向子类对象
重写和重载
重写是在继承关系下,子类对父类同一方法的不同实现
重载是在同一个类中,相同方法名,参数列表不同(参数个数不同,参数类型不同,顺序不同)的方法构成重载。(权限修饰符,返回值类型不同不构成方法重载)
abstract | 抽象类
abstract可修饰的结构:类、方法
abstract修饰的类叫做抽象类,抽象类相当于一个模板,包含了子类的通用特性。它不能被实例化对象,只能被用作子类的超类,所以抽象类必须被继承,才能被使用。
abstract修饰类:抽象类
-
抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
-
开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
abstract修饰方法:抽象方法
-
抽象方法只有方法的声明,没有方法体,不能被调用的,但在该类或子类中是可以调用的
-
包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
-
若子类重写了父类中的所有的抽象方法后,此子类方可实例化
-
若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
接口Interface
接口相当于一种功能规范,要求某接口的所有实现类必须去实现接口中定义的所有功能,才能实例化。
- JDK7及以前:只能定义全局常量和抽象方法
全局常量:public static final的.但是书写时,可以省略不写
抽象方法:public abstract的
-
JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)
-
接口中不能定义构造器的!意味着接口不可以实例化
-
Java开发中,接口通过让类去实现(implements)的方式来使用.
如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
-
Java类可以实现多个接口 —>弥补了Java单继承性的局限性
-
格式:class AA extends BB implements CC,DD,EE
-
接口与接口之间可以继承(因为接口间涉及不到new对象了),而且可以多继承
抽象类与接口
抽象类相当于一个模板,定义了子类的通用特性 。
接口强调功能规范,某接口的所有实现类必须具备接口中所有功能,即重写接口中所有方法才能实例化。
-
相同点:
- 都不能被实例化,接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化
-
不同点:
-
接口强调特定功能的实现,而抽象类强调所属关系。
-
抽象类只能被继承,而且只能单继承。接口需要被实现,而且可以多实现。接口与接口之间可以多继承
-
抽象类可以有构造器,供子类实例化时调用,接口中不能有构造器
-
接口中不可以定义变量即只能定义常量(加上final修饰就会变成常量)。所以接口的属性默认是 public static final 常量,必须赋初值,不能修改;抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值
-
接口的方法默认是 public abstract【1.8可以定义静态,默认方法】;抽象类中可以定义任意修饰符的方法
-
接口中只能声明方法,抽象类可以声明方法,也可以实现方法
-
什么时候使用抽象类和接口?
如果抽取的方法中一些方法想让有默认实现,则使用抽象方法。
由于Java不支持多继承,子类不能够继承多个类,但可以实现多个接口。要实现多重继承则必须用接口。
如果基本功能在不断改变,那么就需要使用抽象类。如果不断改变基本功能并且使用接口,那么就需要改变所有实现了该接口的类。
假如有一个接口,五个实现类,现在的需求可能要往接口加一个方法,这样就要改动五个实现类,但需求只需要改动其中两个实现类,可以再定义一个抽象类去实现这个接口,在抽象类中新增这个方法,然后其他两个实现类实现这个抽象类就好了,或者使用 Java 8 中的新特性,在接口中新增默认方法或者静态方法。
抽象关键字abstract和哪些不可以共存?抽象方法被abstract修饰,抽象方法要被实现,所以不能被private、static、synchronized和native,final等修饰
抽象类中可不可以不定义抽象方法?可以。抽象方法目的仅仅为了不让该类创建对象。
Java中的继承关系加载顺序
代码块:
代码块的作用,用来初始化属性的值,对象
代码块如果有修饰的话,只能使用static。分类:静态代码块 VS 非静态代码块
1)静态代码块
- 内部可以有输出语句
- 随着类的加载而加载,而且只执行一次
- 作用:初始化类的信息
- 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
- 静态代码块的执行要优先于非静态代码块的执行
- 静态代码块内只能调用静态属性,静态方法,不能调用非静态结构
2)非静态代码块
- 内部可以有输出语句
- 随着对象的加载而执行
- 每创建一个对象,就执行一次非静态代码块
- 作用:可以在创建对象时,对对象的属性进行初始化
- 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
- 非静态代码块内可以调用静态的属性,静态的方法,或非静态的属性,非静态的方法
(1)构造代码块的作用:给所有对象进行统一的初始化,对象一建立就运行并且优先于构造函数,比如所有的婴儿出生都会哭。
(2)构造函数的作用:给对应的对象(new )进行初始化。
final
final可修饰类,变量,方法,修饰类,表明该类不可以被继承,修饰基本数据类型,表明该变量的值不可修改,修饰的是引用类型变量,表明该变量引用的始终是同一个变量就行,但对象中的内容是可以修改的;修饰的是方法,表明方法不可以被重写
【static final:全局常量】
static
static修饰的变量,在内存中只有一份,随着Class对象加载存放在堆空间,不依赖对象,通过类就可以调用
static修饰的方法可直接通过类名来调用,静态方法中不能调用非静态属性和非静态方法,非静态方法中可以调用静态方法或静态属性
static修饰代码块:随着类加载而加载,只执行一次,初始化类信息
Super关键字:
super关键字是一个特殊的变量,可以用super主动调用父类的构造方法、 访问父类中的成员。
this关键字:
this可以理解为当前对象,调用当前对象属性和方法,通常可以省略,但是有时为了区分形参和和属性,会用this
instanceof关键字
是用来判断对象是否为某个类的实例, 返回boolean类型的数据
匿名对象
当创建的对象,没有显式的赋给一个变量名,即为匿名对象
特征:匿名对象只能调用一次.一般会用匿名对象传参
Object类
Object类是所有Java类的根父类
如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
Object类中的功能(属性、方法)就具有通用性。
属性:无
方法:
-
equals():默认和==一样,比较两个对象的地址是否相同,一般会进行重写,比较对象的实际内容
-
toString() :默认输出对象类型及其地址,继承Object类一般会重写该方法,打印对象属性信息
-
getClass() :获取当前对象的运行时类对象
-
hashCode() :返回对象的hash值
-
clone() :创建并返回对象的副本
创建同一个类的一个新对象,并将原有对象的属性赋给新对象(浅拷贝),类中的引用类型的属性都是指向同一个地址值,
修改该引用类型属性的内容,原对象中的值也会被改变,如果要实现深拷贝,需要将引用类的属性也实现Cloneable接口, 将该属性也clone一遍。 -
finalize():在GC之前会被自动调用
-
wait() :占有当前对象的线程加入等待队列,直到被其他线程notify()或notifyAll()唤醒
-
notify():随机唤醒当前对象关联的监视器中等待队列中的一个线程
-
notifyAll(): 唤醒当前对象关联的监视器中等待队列中的所有线程
wait() | sleep()
1)原理不同:
- sleep()方法是Thread类的静态方法,是线程用来控制自身流程的,他会让线程暂停执行一段时间,等到时间一到,线程会自动苏醒。它不会释放锁,但会让出CPU给其他线程执行。
- wait()方法是object类中的方法,当线程执行wait()方法时,会让当前持有对象锁的线程释放锁,并让出CPU,进入等待队列中等待,直到其他线程唤醒。也可以给他指定一个时间自动醒来。
2)使用的区域不同:
wait()方法必须放在同步方法或同步代码块中使用,slleep()方法则可以在任何地方使用。
为什么重写equals()还要重写hashcode()
equals()重写主要用来比较元素的内容是否相同,但hashcode()主要用来定位元素放在散列表的哪个桶中,如果不重新写hashcode(),object中的hashcode默认是根据对象的地址值计算的,所以即使new了两个各字段值一样的对象,他们也放在散列表不同位置,违背了如hashset(要保证存储的数据不重复),hashmap(保证节点的key是唯一的)等集合的设计原则,必须使得hash不一样的元素内容一定不一样,所以需要重写hashcode(),一般用来equals的字段都要用来计算hash值。
谈谈对基本数据类型包装类的理解
Java将基本数据类型封装成包装类,主要是提供更多操作基本数值的功能,同时Java是面向对象的,基本类型并不具有对象的性质,如我们在使用集合类型Collection时就一定要使用包装类型而非基本类型),为了让基本类型也具有对象的特征,就出现了包装类型,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
基本数据类型及其包装类分别为:
byte -> Byte; short -> Short; int -> Integer; long -> Long; float -> Float;
double -> Double; boolean -> Boolean; char -> Character;
针对基本数据类型的包装类采用 == 比较是否相等:
Integer部分源码:
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
//Integer内部缓存:-128~127;
static {
....
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
.....
}
/*
如果 -128 < a,b <127 a == b 为 true;
如果a,b不在缓存中,则为 new 新Integer,则a == b 为false;
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
}
Integer a = 100;
Integer b = 100;
System.out.println(a == b); //true
Integer a = 200;
Integer b = 200;
System.out.println(a == b);//false
/*
原因:Integer类中存在内部缓存:大小为 -128~127;当变量初始化的值在其中时,底层会直接将缓存中的地址赋给变量,所以在 == 比较时两者地址值是一样的,显示true;当变量初始化的值不在其中时,底层会new出新的Integer对象,将其赋值给变量,只要new对象,必有新内存的开辟,两者存储地址不同。在 == 比较时会显示flase;
*/
final、finalize、finally区别
1)final为关键字;final可修饰类,变量,方法,修饰类,表明该类不可以被继承,修饰基本数据类型,表明该变量的值不可修改,修饰的是引用类型变量,表明该变量引用的始终是同一个变量就行,但对象中的内容是可以修改的;修饰的是方法,表明方法不可以被重写
2)finalize()为方法; gc之前会调用finalize()进行一些操作
3)finally{}用于标识代码块,与try{}进行配合,不论try中的代码执行完或还是出异常退出,该代码块之中的程序必定会进行,一般资源的释放和解锁操作都在这里完成
==和equals()的区别
1)==属于运算符,可以使用在基本数据类型变量和引用数据类型变量中,如果比较的是基本数据类型变量,比较两个变量保存的数据是否相等;如果比较的是引用数据类型变量,比较两个对象的地址值是否相同
2)equals()属于方法,只使用于引用数据类型中,如果没有进行重写,则比较的是两个对象的地址,通常会进行重写比较两个对象的内容是否相同
谈谈对String的了解
-
String是一个不可变的字符串,只要对当前操作的字符串进行修改,就会新建一个字符串;
-
String是一个final类,不可继承
-
String底层jdk8使用char[]型数组存储,jsk9改成byte[];(不能直接操作该数组,源码将其私有,没有提供数组的get/set方法,且被final修饰,不能指向别的字符串)
-
String实现了Serializable接口,表示字符串是支持序列化的;实现Comparable接口,表示String可比较大小;
String | StringBuffer | StringBuilder
String是一个不可变的字符序列,底层使用char[]存储,并使用private final修饰,不能直接操作该数组;只要对当前字符串进行修改,就会新建一个字符串;
StringBuffer、StringBuilder是一个可变的字符序列,底层使用char[]存储,未使用final修饰,可直接操作字符串,StringBuffer中的方法都用了sync同步代码块,线程安全,效率低;StringBuilder中的方法没有,线程不安全的,效率高;
- StringBuffer和StringBuilder创建对象时底层都是创建长度为16的数组,当添加的数据装不下时就需要扩容,默认扩容为原来容量的两倍+2,同时将原有数据复制到新数组中
- 开发中,建议指定容量,避免扩容,同时建议少使用String,每次都要新建,效率低
字符串常量池
在JVM中,为了减少相同字符串的重复创建,达到节省内存的目的。会单独开辟一块内存,用于保存字符串常量,这个内存区域被叫做字符串常量池
String中的intern()方法
jdk1.6中,将这个字符串对象尝试放入池中
- 如果池中有,则不会放入,返回串池中已有对象的地址
- 如果没有,会把该对象复制一份,放入串池中,并返回串池中的对象地址
jdk1.7起,将这个字符串对象尝试放入池中
-
如果池中有,则不会放入,返回串池中已有对象的地址
-
如果没有,则把对象的引用地址复制一份,放入池中,并返回池中的引用地址
什么是反射
反射:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取类的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
为什么能利用反射
加载完类之后会将类的元信息都存放在方法区,并在堆内存中生成一个Class类型的对象(一个 类只有一个Class对象),作为元信息的访问接口。通过反射相关API就可以获取并操作该类的属性,方法等信息,创建实例等Java反射机制提供的功能
在运行时【获取任意一个类的属性和方法,创建该类的对象, 判断对象所属的类,调用任意一个对象的成员变量和方法,获取泛型信息,处理注解,动态代理】Class对象的理解
Class本身也是一个类
Class 对象只能由系统建立对象
一个加载的类在 JVM 中只会有一个Class实例
一个Class对象对应的是一个加载到JVM中的一个.class文件
每个类的实例都会记得自己是由哪个 Class 实例所生成
通过Class可以完整地得到一个类中的所有被加载的结构
Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的 Class对象Class实例可以是哪些结构的说明:接口,类,基本数据类型,void,数组,Class
获取Class对象的方式
方式一:已知具体的类,调用运行时类的属性:.class
方式二:已知某个类的对象,通过运行时类的对象,调用getClass()
方式三:已知一个类的全类名,调用Class的静态方法:forName(String classPath)哪里用到了反射机制
JDBC中,利用反射动态加载了数据库驱动程序。
Web服务器中利用反射调用了Sevlet的服务方法。
Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。
很多框架都用到反射机制,注入属性,调用方法,如Spring。
异常
异常:在Java语言中,将程序执行中发生的不正常情况称为“异常” 。
异常可以分为两类:
- Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源 耗尽等严重情况。如:StackOverflowError和OOM。一般不编写针对性 的代码进行处理。
- Exception: 因编程错误或偶然的外在因素导致的一般性问题,可以使 用针对性的代码进行处理。其分为受检查异常,不受检查异常。
1)受检查的异常:在编译时被强制检查的异常称为"受检查的异常"。即在方法的声明中声明的异常。
2)不受检查的异常:在方法的声明中没有声明,但在方法的运行过程中发生的各种异常被称为"不被检查的异常"。
a..一般不是因程序问题而引起的错误情况是受检查异常,如文件不存在,不被检查异常通常是由程序错误引起的,在程序运行起来才能知道,如没有考虑对象为空的情况而出现的空指针异常
b.受检查异常会被编译器自动捕获,所以必须捕获或声明所有编译时异常。否则编译无法通过,而非受检查异常可以捕获或者不捕获。开发中,由于运行时异常比较常见,一般不作任何处理,若全处理可能会对程序的可读性和运行效率产生影响。
c.
Java中所有异常或者错误都继承Throwable,其子类有:
Error:程序无法处理的异常,比如内存不够,字节码不合法等。
Exception:这个属于应用程序级别的异常,分为运行时异常和编译时异常。RuntimeException继承了Exception,RuntimeException子类对象都属于运行时异常,其他属于编译时异常
C.处理受检查异常的方式throws、try-catch-finally
捕获异常的方式:
try-catch-finally
- 使用try将可能出现异常的代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配
- 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理,一旦处理完成,就跳出的try-catch结构(在没有写finally的情况)。继续执行其后的代码
- finally是可选的,如果有finally,finally中声明的一定是会被执行的代码。即使catch中又出现了异常,try中有return语句,catch中有return语句等情况。【像数据库连接,网络编程Socket等资源,JVM是不能自动回收的,我们需要手动的进行资源的释放。此时资源的释放,就需要声明在finally中。】
- catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓。否则要求子类一定声明在父类的上面,不然报错
- 常用的异常对象处理方式:String getMessage()、printStackTrace()在try结构中声明的变量,
- 在出了try结构后,就不能再调用了。try-catch-finally结构可以嵌套
- 子类重写父类的方法抛出的异常类型不大于父类被重写的方法抛出的异常
throws + 异常类型
"throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
方法体执行时出现异常,就会生成一个异常类对象,此对象满足throws后异常类型时,就会被抛出,异常代码后续的代码,就不再执行
a.
- 使用try-catch-finally处理编译时异常,使得程序在编译的时候就不在报错,但是在运行时仍可能报错,相当于把编译时的异常延迟到运行的时候
b. try-catch-finally真正的将异常给处理掉了,throws的方式只是将异常抛给了方法的调用者。并没有真正将异常处理掉
c.开发中如何选择使用try-catch-finally 还是使用throws:
- 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws。意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理。
- 如果多个方法嵌套调用,则最层方法里的所有嵌套的方法使用throws的方式处理,而最外层方法使用try-catch-finally方式进行处理
d.throw | throws
- throw:异常的生成阶段使用,手动抛出异常对象
- throws:异常的处理方式,声明方法可能要跑出的各种异常类
泛型
泛型允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如, 继承或实现这个接口,用这个类型声明变量、创建对象时)通过类型擦除确定(即传入实际的类型参数,也称为类型实参)。
为什么要有泛型呢,直接Object不是也可以存储数据吗?
1)解决元素存储的安全性问题,在没有使用泛型之前,任何类型元素都可以放在集合中,类型不安全。在实例化集合时,当指明具体的泛型类型,会进行类型检查,保证数据安全
2)避免强转,比如我们在使用List时, 如果我们不使用泛型, 当从List中取出元素时, 其类型会是默认的Object, 我们必须将其向下转型为String才能使用。这其中可能出现ClassCastException.使用泛型,就可以保证存入和取出的类型都是String,避免了强转。( 如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型)
枚举类
类的对象是明确的,只有有限个
枚举类对象的属性不应允许被改动,所以应该使用private final修饰
枚举类常用方法:
- values()方法:返回所有枚举对象构成的数组
- valueOf(String str):返回枚举类中对象名是objName的对象,若没有objName,则抛出IllegalArgumentException
- toString():返回当前枚举类对象常量的名称
常用日期类
LocalDate、LocalTime、LocalDateTime
SimpleDateFormat:格式化或解析日期、时间
1.两种构造器的使用
SimpleDateFormat() : 此构造器返回默认的日期格式:20-6-8 下午12:08
SimpleDateFormat(String) : 此构造器返回指定日期格式(常用:yyyy-MM-dd HH:mm:ss)
2.两种方法的使用
format(date) : 格式化: 日期—>字符串
parse(date) : 解析: 字符串—>日期
comparable | comparator
Comparable和Comparator都可以定义排序规则;
Comparable:是一个接口,通过实现Comparable接口,重写compareTo(obj)方法,从而保证Comparable接口实现类的对象在任何位置都可以比较大小。
Comparator:是一个类,可以创建为对象制定临时排序规则。
Comparator类属于临时性的比较。 当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来排序
持续更新中。。。