赞
踩
答:jdk是开发工具,给开发者用的;jre是运行时环境,用来运行java程序的;如果你需要编写java程序,需要安装JDK。如果你需要运行java程序,只需要安装JRE就可以了。
JDK包含JRE包含JVM
JDK: Java development kit Java 开发工具
JRE: Java runtime environment Java运行是环境
JDK包含JRE,JRE包含JVM,安装Eclipse你会发现它是运行不起来 是会报错的,只有安装了JDK,配置好了环境变量和path才可以运行成功。
【参考】
运行过程:Java文件经过编译的.class的字节码文件,Java虚拟机JVM 再把字节码文件翻译成机器码,计算机再执行这个机器码,Java程序就得到运行了。
到处运行:靠的是jvm,不同平台下的jvm是不同的,但他们都能运行这个相同的字节码文件,Java 虚拟机再把这个字节码文件->当前平台下的机器码,java文件就可以实现到处运行。
JVM在执行字节码文件时,是读一行执行一行的,所以是解释型语言;
解释型语言运行得都比较慢。
【参考】
其实就是被人写好的可以实现某种特定功能的函数,开发者只需要调用它提供的接口,然后传入它规定的参数,这个函数就会实现相应的功能。
一开始javax是对JavaAPI的扩展,后来也逐渐把这些扩展移到了java下面,所以现在可以说java包和javax是没有区别的
重写:override 重载:overload
重载: 发生在同一个类中,比如写构造函数的时候一般会写无参构造方法和有参构造方法,方法名必须相同,参数列表不同(类型不同、个数不同、顺序),方法返回值也可以不同。
重写: 发生在父子类中,子类重写父类的方法,方法名、参数列表必须相同(返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类)如果父类
方法访问修饰符为 private 则子类就不能重写该方法。
1. 封装:将类的某些属性、信息封装在类的内部,不允许外部直接访问,而是通过类提供的方法来对类内部的信息进行访问或操作。
2. 继承:子类继承父类,是一种is-a的关系,子类继承到父类属性和方法,在父类基础上进行拓展,添加新的属性和方法,也可以重写父类的方法(父类的private属性和方法、构造方法不能被重写)
has-a:用来判断类与成员的关系,即类拥有某种属性或某种方法
3. 多态:对于同一个方法,不同的参数列表、不同的参数类型传进来可以有不同的处理方式,主要体现在继承和重载
继承:在继承的时候,一个父类可能有多个子类,子类A和子类B都重写了父类的同一个方法,但是子类AB的对象在调用这个方法的时候,都会根据当前子类重写的逻辑来处理,这就是多态。(多态也可以通过接口这种形式实现)
重载:在同一个类里,同一个方法名,不同的参数传进来,会有不同的处理方式,这就是重载;
多态的一种体现就是,你只定义了一个方法和一个参数类型,但却可以处理多种形态的参数,这就是多态。
多态比较难理解
向上:将子类对象赋值给父类对象(父类引用指向子类对象),在日常开发中我们大量使用向上转型,这样会充分发挥多态的特性
向下:将父类对象赋值给子类对象,这个是否需要强制类型转换,否则会编译报错(编译通过了也不一定能正确执行)
关键字:enum,调用时和静态常量一样,它能够更好地限定语义和判断每个值的合理性,它把值限定在了这几个值的范围之内,如果超出了这几个值的范围,在编译的时候就会报错,就能更好地检查每个值的合理性。
不可以
对对象进行初始化
可以。会默认执行无参构造方法。
1.与类名相同,可以有无参和有参构造方法
2. 没有返回值,但不能用 void 声明构造函数;
3. 生成类的对象时自动执行,无需调用。
帮助子类做初始化工作
子类的对象在构造之前,如果没有用 super() 来调用父类特定的构造方法,则会先调用父类的无参构造方法。这个时候,如果父类中没有无参构造方法,那编译就会发生错误。
Java的反射是Java能够动态获取信息、动态调用对象方法的一种机制。
在运行状态中,对于任何一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意方法和属性
Java对象在以下情况下需要
对于不想进行序列化的变量,使用 transient 关键字修饰;
当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复;
transient 只能修饰变量,不能修饰类和方法
同:都是面向对象的语言,都支持封装、继承和多态
异:
对数据进行拷贝
实现Clonable接口,实现clone()方法
Java只有值传递,
实参:要传递给方法的实际参数,在方法外面
形参:方法名的参数列表,在方法里面
值传递:对形参的修改不会影响到实参的值
引用传递:对形参的修改会影响到实参的值
JVM中划分了好几块内存区域,其中有一个栈空间和一个堆空间,创建的所有对象存放在堆中,基本数据类型和局部变量是存放在栈中的。
当操作对象时,对象是存放在堆中的,我们拿到的只是这个对象的引用,通过对象的引用,就可以操作对象;如果是基本数据类型,那就复制一份值,传递给形参;如果是引用类型,那就将引用赋值一份传递给形参,形参拿到的始终是一个副本,无论如何都无法通过形参改变实参,毕竟形参只是操作的副本而已
概念:值传递:对形参的修改不会影响到实参的值;引用传递:对形参的修改会影响到实参的值
原因:实参传递给形参时,形参拿到的始终都是一个副本而不是实参本身,所以无论形参怎么改变都不会影响到实参的值,所以是Java只有值传递
都用用来判断相等的
“==”:判断两个对象的地址是否相等
equals:永远都是比较内容。默认执行 = =;如果重写了就是比较对象的内容,String重写了equals,所以String在用equals的时候比较的是内容。
不一定。
hashCode:获取哈希码,由哈希码来确定对象在哈希表中的索引位置(Java的任何类都有hashCode函数)
equals是比较两个对象是否想等的,hashCode相同只能说明它们的哈希码相同,被散列到相同的索引位置,但并不能说明两个对象相等。
1)变量:
1)基本数据类型:数值初始化了之后不能被修改;
2)引用类型:初始化之后就不能再指向其他对象了
2)类:不可派生类,不能再被继承,final类中的所有成员方法也都被隐式地指定为final方法
3)方法:方法锁定,对于方法所在的类,它的子类是不能够修改这个方法(所有private方法都隐式地指定为final)
不能修饰接口和抽象类 interface abstractClass
将类的成员属性和成员变量定义为static时,就意味着将他们声明为静态属性和静态方法,,不用创建对象,直接通过类名就可以调用。
只能修饰变量和方法,被static修饰的成员随着类的加载而加载,生命周期和类一样;
没有被static修饰的变量和方法只能通过创建对象的形式调用(new 类名.方法名的方式),只能以对象的形式存在,被static修饰的成员是优先于对象存在的,静态的方法在调用非静态的成员时,非静态的成员还没有存在,所以不能调用非静态的变量或方法
由于静态方法可以不通过对象进行调用,因此在静态方法里,不能调用其他非
静态变量,也不可以访问非静态变量成员。
在一个类中定义的类就是内部类,不同的内部类其实就是作用范围不一样
public:类在本程序的任何地方都能使用,不加则代表这个类只能在所处的包下访问,
概念:成员变量是类的成员变量,局部变量是方法内的局部变量
new
方法的返回值:执行某个方法后返回的数据
返回值的作用:用于接收某个过程的结果,这个结果可能会被用于其他操作
Stream提供了大量的方法进行聚集操作,
中间方法:中间操作允许流保持打开状态,并允许直接调用后续方法。
min()max()abs()round()pow()exp()
round():round(-1.5)等于多少——-1
效率不一样
有8类,又4类:
int 4字节,32位,1 位用来存符号,[- 231, 231-1]
对于8 种数据类型:
byte:0
short:0
int:0
long:0L
float:0.0F
double:0.0
char:‘\u0000’ 空
boolean:false
不属于
与final关键字有关
String:是用final关键字来保存字符串的,所以不可变;修改的时候,都会新创建一个String对象,然后再将当前对象的指针指向新的String对象。
StringBuilder 与StringBuffer 都继承自 AbstractStringBuilder 类, AbstractStringBuilder 没有用 final 关键字修饰,所以这两种对象都是可变的。
String < StringBuffer <StringBuilder
地址不一样
public static void main(String[] args) {
String a = "i";
String aa = new String("i");
if(a == aa) System.out.println("==");//false
if(a.equals(aa)) System.out.println("equals");//true
}
用可变的字符串类,StringBuilder & StringBuffer都可以,然后调用reverse()方法
1)形式:字符常量是单引号引起的一个字符 字符串常量是双引号引起的
若干个字符
2)含义:字符常量相当于一个整形值( ASCII 值),可以参加表达式运算 字
符串常量代表一个地址值(该字符串在内存中存放位置)
3)内存:字符常量只占 2 个字节 字符串常量占若干个字节(至少一个
字符结束标志) (== char 在 Java 中占两个字节==)
关键字是abstract
接口–Interface 抽象类–Abstract Class
概念:抽象类是对对象的抽象,接口是对动作的抽象;类在实现接口和抽象类的时候用的关键词不一样,接口是implements实现,抽象类是extends;
区别:
1)实例化:接口不能实例化,因为它不是对象,只有对象才能实例化;
抽象类不能被实例化,因为一个抽象类中没有"足够的信息"来描绘一个对象
2)类的实现:类可以实现多个接口,但只能继承自一个抽象类(单继承)
抽象类可以有具体的方法,不一定都是抽象方法
1)实例化:普通类可以实例化,抽象类不能,因为抽象类中没有"足够的信息"来描绘一个对象
2)继承:都能被继承,继承抽象类的子类必须要重写继承的方法(除非子类也是抽象类)
3)final:接口中的实例变量默认是 final 类型的,而抽象类中则不一定
【参考】
不能,final不可派生类,抽象类就没有意义了;也不能修饰接口
Java集合类主要派生自两大集合类:Collection 和 Map,Collection 又派生出 List、Set、Queue三大集合类;Java 所有集合类都是这四个接口的实现。
因为集合类不够具体,克隆和序列化是跟具体的语义和含义相关的
用来对集合类进行遍历的,在遍历的过程中可以对集合类内的元素进行操作
常用方法:
List、Set、Queue、Map
首先,List和Set继承自Collection接口,都是单列集合
其次,Map是双列集合,存储的是键值对,key不能重复
不安全:大部分不安全ArrayList、LinkList、HashMap、TreeMap、HashSet、TreeSet
安全:Vector、HashTable
基本数据类型 - 包装类之间的转换 - “箱”-包装类
自动装箱:将基本类型赋值给包装类型,就完成了自动装箱
自动拆箱:将包装类型赋值给基本数据类型,就完成了自动拆箱
包装类是为了更好地面向对象,Java是一种面向对象的语言,但8种数据类型中的任何一种都不是对象,没有对象的特性。为了更好的面向对象,Java 为每一个基本数据类型都定义了一个包装类。
缓存池也叫常量池,是事先存储一些常用数据用于节省空间的一种技术。大部分的包装类型都实现和缓存池。
在自动装箱时,如果基本数据类型的值在缓存的范围内,则不会重新创建对象,而是复用缓存池中实现创建好的对象。
Integer类型默认缓存了[-128, 127]的值,只要是这个范围的值自动装箱,都会返回相同的对象;所以如果是包装类型之间进行判断的话,要用equals(),不能用==,因为不同包装类缓存的范围不一样。
自动装箱:调用了包装类型的valueOf()方法
自动拆箱:调用了xxxValue()方法(如intVaule())
所以,在创建包装类对象时,要么使用自动装箱的方法,要么使用valueOf()方法,而不要直接new,因为vauleOf()方法利用了缓存,而直接new是直接在堆里创建对象,没有利用缓存。
(小学刚开始学数学的时候)不同单位的数据是不能直接比较的,如果要比较,必须先进行单位的换算,不同的数据类型之间的比较也要先进行数据类型的转换。
为了不损失精度,一般都是低精度向高精度转换,所以要把Integer 转成 Double,然后再进行比较。
int 基本数据类型,Integer 是包装类;== 是基本数据类型之间的比较,所以Integer 会自动拆箱成int类型,然后再进行比较;返回 true or false。
Set无序,不可重复的集合;
List有序,可以重复的集合。
都由Collections工具类派生的
实现:ArrayList的底层是用数组来实现的
随机访问:O(1)
首次插入:默认第一次插入元素时创建大小为10的数组,超出限制时会增加50%的容量,并且数据以 System.arraycopy() 复制到新的数组,因此最好能给出数组大小的预估值。
增删:数组末尾效率较高,按下标,需要移动数据,时间复杂度O(n),影响到性能。
底层实现:ArrayList的实现是基于数组,LinkedList的实现是基于双向链表;
时间复杂度:随机访问O(1);O(N)
插入删除:链表会方便一点,不需要移动
内存:LinkedList比ArrayList更占内存,上一个指针,下一个指针
同:
异:
ArrayList线程不安全;Vector线程安全(使用了synchronized 方法)
ArrayList < Vector < inkedList
不一定
HashSet依赖与HashMap,它的值是存在HashMap的key中的,所以key不允许重复。
HashSet是基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。
它封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
基本原理:TreeMap
最大特点:有序
特殊方法:
celing: 返回>=传入值的最小值
floor: 返回<= 传入值的最大值
higher: 比传入值大的
lower: 比传入值小的
desendingSet: TreeSet倒序
pollFirst: 返回并删除第一个
pollLast: 返回并删除最后一个
使用场景:对调接口返回的数据,按某一维度排序、进行展示或别的消费??
都是派生自Set包装类的,无序、都不能重复;都是线程不安全
区别:TreeSet可以实现排序,按自然顺序存储;HashSet是乱序
存储:TreeSet要自然排序,所以不能为null;HashSet就可以
实现:HashSet底层是采用哈希表实现的,而TreeSet底层是采用红黑树实现的
两个接口 Dueue、BlockingQueue;抽象类 AbstractQueue
代表双端队列的能力
代表阻塞队列的能力,可以用来实现生产者、消费者模型
抽象队列,提供了默认的实现
add():插入失败,抛异常
offer():插入失败返回false
element():获取失败,抛异常
peek():获取失败,返回null
remove():删除失败,异常
poll():删除失败,null
“操作失败时:熟悉的单词抛异常,不熟悉的单词返回false”
抛异常:add remove element
返回false:offer poll peek
BlockingQueue 提供了4 组不同的方法用于插入、移除以及对队列中的元素进行检查;
BlockingQueue是一个接口,它的实现类有ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue。它们的区别主要体现在存储结构上或对元素操作上的不同,但是对于put与take操作的原理是类似的。下面以ArrayBlockingQueue为例,来说明BlockingQueue的实现原理。
首次扩容:先判断数组是否为空,若数组为空则进行第一次扩容(resize);
计算索引:通过hash算法,计算键值对在数组中的索引–哈希码;
插入数据:
如果当前位置元素为空,则直接插入数据;
如果当前位置元素非空,且key已存在,则直接覆盖其value;
如果当前位置元素非空,且key不存在,则将数据链到链表末端;
若链表长度达到8,则将链表转换成红黑树,并将数据插入树中;
再次扩容:如果数组中元素个数(size)超过threshold,则再次进行扩容操作。
1. Collections工具类,将线程不安全的Map包装成线程安全的Map;
2. 使用ConcurrentHashMap
不建议使用Hashtable,虽然Hashtable是线程安全的,但是性能较差。
都是无序、不能重复集合;
Set存的是元素,Map存的元素的键值对,Map的key是一个Set集合,所以key不能重复。
目的:优化查找性能
JDK7:数组+链表,hashCode哈希码冲突,链表太长,查找的时候时间消耗很高,降低了查找的效率,O(n)
JDK8:数组+链表+红黑树,链表长度超过8的时候,采用红黑树存储,O(logn),查找效率得到优化
hash算法:取模,
存–put:调用hashCode获取哈希码,进而得到bucket的位置,存到相应链表
取–get:调用hashCode获取哈希码,然后到对应的bucket中查找
1)先扩充数组:
首先:判断是否要扩容,是则按2的n次方扩充,更大的数组能在一定程度上减少碰撞(还有一个说法就是使用位运算代替取模预算(据说提升了5~8倍))
判断是否要扩容:负载因子,元素个数与数组容量之比
2)链表转红黑树:(检查链表长度转换成红黑树之前,还会先检测当前数组数组是否到达一个阈值(64),如果没有到达这个容量,会放弃转换,先去扩充数组)为了解决碰撞,数组中的元素是单向链表类型。当链表长度到达一个阈值时(7或8),会将链表转换成红黑树提高性能(而当链表长度缩小到另一个阈值时(6),又会将红黑树转换回单向链表提高性能。)
首先,用红黑树是为了优化查找效率,B树一个节点可以存好多个数据,在数据量不是很多的情况下,数据都会“挤在”一个结点里面,这时候优化的效果并不是特别的好;而且必输一般是用在外存上的,B树的高度就是磁盘存取的次数,磁盘存储本来就慢,就更达不到优化查找效率的效果了。
拿取模来讲,如果取模的结构都是1,就都会存到对应的单向链表中;
当链表长度到达8时,会将链表转换成红黑树提高查找性能;
而当链表长度缩小到另一个阈值时,又会将红黑树转换回单向链表提高性能。
**为了提高查找效率,遍历树为O(logn),链表的遍历为O(n)
还不是很理解
1)线程安全:
2)性能:
3)null作为key,value
HashMap是线程不安全的
ConcurrentMap是线程安全的
JDK7:Segment数组+链表
JDK8:Node数组+链表+红黑树
还有呢
要求有序
内存的存储与插入的顺序保持一致,
使用双向链表来维护key-value对的顺序
需要维护元素的插入顺序,因此性能略低于HashMap的性能,但因为它以链表来维护内部顺序,所以在迭代访问Map里的全部元素时将有较好的性能。
LinkedHashMap可以避免对HashMap、Hashtable里的key-value对进行排序(只要插入key-value对时保持顺序即可),同时又可避免使用TreeMap所增加的成本。
1)LinkedHashMap继承于HashMap,很多方法直接继承自HashMap,
2)特点是维持插入顺序,所以维护的是双向链表,HashMap是单向链表
对key进行排序,升序降序可以自定义Comparator比较器;基于红黑树(Red-Black tree)实现的,时间复杂度是O(logn)。
红黑树的节点是Entry类型的,它包含了红黑树的6个基本组成:key、value、left、right、parent和color。
结点为空色或者黑色;红色代表活跃的点;
根叶黑:根只能为黑色,每个叶子结点都是黑色的空结点;
不红红:不能有连续的红色结点
黑路同:从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点;
所有的异常类都继承自Throwable,Throwable有两个子类:Error、Exception
try:用于捕获异常,里面写的是业务逻辑的代码。(其后可接零个或多个 catch 块,如果没有 catch块,则必须跟一个 finally 块。)
catch:用于处理 try 捕获到的异常。
finally:无论是否捕获或处理异常,finally 块里的语句都会被执行。finally可省略
在以下 4 种特殊情况下,finally 块不会被执行:
1.在 finally 语句块中发生了异常。
2.在前面的代码中用了 System.exit()退出程序。
3.程序所在的线程死亡。
4.关闭 CPU。
finally
当在 try 块或 catch 块中遇到 return 语句时,会先执行finally再return。
修饰符,修饰类、方法、变量
异常处理的finally块
动词,方法名,在垃圾回收的时候会用到,如果这个对象没有被引用,那就会对这个对象效用finalize方法,一个对象如果没有被引用的话,那分配给它的内存就会被回收。
执行过程:首先需要准备好编译好的 Java 字节码文件(即class文件),计算机要运行程序需要先通过一定方式(类加载器)将 class 文件加载到内存中(运行时数据区),但是字节码文件是JVM定义的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解释器(执行引擎)将字节码翻译成特定的操作系统指令集交给 CPU 去执行,这个过程中会需要调用到一些不同语言为 Java 提供的接口(例如驱动、地图制作等),这就用到了本地 Native 接口(本地库接口)。
JVM在执行Java程序的过程中会把它所管理的内存划分为几个数据区:分别是
本地方法栈、程序计数器、虚拟机栈、|| 堆、数据区
本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是
本地方法栈:为虚拟机使用到的本地(Native)方法服务
虚拟机栈:为虚拟机执行Java方法(也就是字节码)服务
方法区和Java堆,它用于存储已被虚拟机加载的类和变量
PC是控制程序跳转的
没有PC,程序不能正常地跳转,进程也不能够进行正常的切换。
栈
.java -> .class -> (JVM) 机器码
对象实例化过程,就是执行类构造函数的过程,会根据实例化时是否有参数、有几个参数来匹配构造方法,进而进行实例化。
栈外:元空间占用的是本地内存
类加载器:
双亲委派模型:要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。
(Java平台通过委派模型去加载类。每个类加载器都有一个父加载器。)
1、当需要加载类时,会优先委派当前所在的类的加载器的父加载器去加载这个类。
2、如果父加载器无法加载到这个类时,再尝试在当前所在的类的加载器中加载这个类。
会被破坏,但更具体的我就没有了解得那么多了。
垃圾回收是对对象的回收,对象保存在堆中,所以垃圾回收是发生在堆中的
原先,一块内存分配给了一个对象,但是这个对象在之后就再也没有被引用过了,那这块内存就变成了垃圾,Java虚拟机的一个系统级线程就会自动回收这个内存块。
引用计数算法:没有使用根集
该算法使用引用计数器来区分存活对象和不再使用的对象。
每个对象对应一个引用计数器,初始时置为1;当对象被赋给任意变量时,引用计数器每次加1;当对象出了作用域后(该对象丢弃不再使用),引用计数器减1,一旦引用计数器为0,对象就满足了垃圾收集的条件。比如for循环里的循环变量i
可达性分析算法:GCRoot
从根对象开始,根据引用关系向下搜索,就是类似于图理论里面,一个对象就是一个结点,边就是引用关系,没有引用关系就没有边;如果根节点到某个点不可达,那就是他们之间没有引用关系,那这个对象就可以被回收。
分为新生代和老年代,回收的都是新生代;老年代最多是15代
更详细的我还需要花一些时间
IO用于实现对数据的输入与输出操作,Java把不同的输入/输出源抽象表述为Stream,然后就可以重新对数据进行操作。
举例:比如在list和int[]数组互相转换的时候,就会用到stream();如果是list转为int[]数组…
不是直接把数据读入内存,而是分多次读入。
缓冲区:维护了一个缓冲区,通过与缓冲区的交互,减少内存与设备的交互次数。
缓冲区是,缓冲区满了之后才能够读出;当缓冲区为空的时候才能写入。
多次对缓冲区的读出写入,从而实现大文件的读取。
可以,但最多一个public 修饰,且与文件名相同
访问权限的修饰词有三个:public、protected、private;形成四个等级的访问权限:public、default 无修饰词、protected、private。
修饰类:2 种权限:public、default
区别:作用范围在包这个等级,前:任意包下的任意类;后:当前包下的类。
修饰成员变量 / 成员方法:4 种权限
public:任意包的任意成员;default:当前包下的成员;protected:当前类的内部成员;private:只能被内部类的成员访问。
成员变量可以理解为全局变量,
作用范围:成员变量是整个类之内;局部变量是某个方法的范围内
默认初始值:成员变量无;局部-有
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。