理论篇-Java中一些零碎的知识点
1. Java中length,length方法,size方法区别
2. isEmpty方法
3. Queue中 add/offer,element/peek,remove/poll方法
4. Set用法
5. static代码块、构造代码块和构造方法
- static代码块:类加载时就会调用,仅执行一次,没有名字、参数和返回值。
- 构造代码块:在对象初始化时执行,有几个对象执行几次,晚于static代码块,早于构造函数,没有名字、参数和返回值。
- 构造方法:在对象初始化时执行,有几个对象执行几次,没有返回值。
public class Constructor { public static void main(String[] args) {
System.out.println("创建第一个对象:");
Test test1 = new Test();
}
} class Test {
// 静态代码块1
static {
System.out.println("静态代码块1");
}
// 构造代码块1:
{
System.out.println("构造代码块1");
} // 构造函数1
public Test() {
System.out.println("无参构造函数");
} }
运行结果:
创建第一个对象:
静态代码块1
构造代码块1
无参构造函数
6. 重写equals和hashcode方法
7. 序列化与反序列化
8. transient关键字
9. 枚举enum
- 是什么:枚举是一个特殊的类,有实例字段、构造器和方法, 因此它是可拓展的。
- 何时用:定义固定常量集合的时候,等价于public static final 变量名,如下所示:
public enum Index {
ZERO(0),
ONE(1),
TWO(2); private int index; Index(int index) {
this.index = index;
} public int getIndex() {
return index;
}
}
10. ThreadLocal局部变量实现线程同步
11. 原子类Automic
12. 线程池
- 每次新建对象性能差。
- 线程缺乏统一的管理,可能无限制的新建线程,相互竞争,可能占用过多的资源导致OOM。
- 缺乏更多功能,如定时执行、定期执行、线程中断。
- 重用存在的线程,减少对象创建、消亡的开销,提高响应速度。
- 可有效控制最大并发线程数,降低资源消耗,同时避免过多资源竞争。
- 提高线程的可管理性,可以进行统一的分配,调优和监控。
- 提供定时执行、定期执行、单线程、并发数控制等功能。
- newFixedThreadPool和newSingleThreadExecutor:
- newCachedThreadPool和newScheduledThreadPool:
public class ScheduledThreadPoolTest {
public static void main(String[] args) throws InterruptedException {
// 创建大小为5的线程池
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5,new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(false).build()); for (int i = 0; i < 3; i++) {
Task worker = new Task("task-" + i);
// 只执行一次
// scheduledThreadPool.schedule(worker, 5, TimeUnit.SECONDS);
// 周期性执行,每5秒执行一次
scheduledThreadPool.scheduleAtFixedRate(worker, 0,5, TimeUnit.SECONDS);
}
Thread.sleep(10000);
System.out.println("Shutting down executor...");
// 关闭线程池
scheduledThreadPool.shutdown();
boolean isDone;
// 等待线程池终止
do {
isDone = scheduledThreadPool.awaitTermination(1, TimeUnit.DAYS);
System.out.println("awaitTermination...");
}
while(!isDone)
System.out.println("Finished all threads");
}
}
class Task implements Runnable {
private String name; public Task(String name) {
this.name = name;
} @Override
public void run() {
System.out.println("name = " + name + ", startTime = " + new Date());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("name = " + name + ", endTime = " + new Date());
}
}
13. Java中自动装箱与拆箱(autoboxing and unboxing)
Integer total = 99;//自动装箱
int totalprim = total;//自动拆箱
自动装箱拆箱的类型为八种基本类型:
基本类型和包装器类型有许多不同点。
14. Java中的隐式转换和强制转换
由低到高
byte a = 1;
short b = a;
int c = b;
long d = c; float f = 2f;
double g = f;
由高到低
默认强制转换
15. Java中String、StringBuffer、StringBuilder
String:不可变长的字符序列;
StringBuffer:可变的字符序列,线程安全,效率低;
StringBuilder:可变的字符序列,线程不安全,效率高。
16. 向下转型和向上转型
为了共用一套代码:
public class PetFactory {
private PetFactory() {
}
public static Pet getPet(String type) throws Exception {
Pet pet = null;
switch (type) {
case "dog":
pet = new Dog();
break;
case "cat":
pet = new Cat();
break;
default:
throw new Exception();
}
return pet;
}
}
public void example(String type){
Pet pet = PetFactory.getPet(type); //向上转型
playWithPet(pet);//公共的
if(pet instanceOf Dog){
Dog snoopy = (Dog) pet; //向下转型
snoopy.sitDown();
} else {
Cat white = (Cat) pet; //向下转型
white.climb();
}
}
17. String hashCode 方法选择数字31作为乘子
- 31是一个不大不小的质数,质数的特性(只有1和自己是因子)能够使得它和其他数相乘后得到的结果比其他方式更容易产成唯一性,降低哈希算法的冲突率;
- 31可以被 JVM 优化,31 * i = (i << 5) - i。
18. Java内部类访问外部类局部变量必须声明为final
编译后的内部类和外部类各一个class文件,内部类访问外部类局部变量实际是复制了一份,为了避免数据的不一致性,设置局部变量为final。
19. 类的加载
Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息,通过该元信息可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户通过元信息间接调用Class对象的功能。
在Java中,类装载器把一个类装入JVM中,要经过以下步骤:
- 装载:查找和导入Class文件;
- 链接:把类的二进制数据合并到JRE中; (a)校验:检查载入Class文件数据的正确性; (b)准备:给类的静态变量分配存储空间; (c)解析:将符号引用转成直接引用;
- 初始化:对类的静态变量,静态代码块执行初始化操作
20. 双亲委派模式
- 原理
类加载器有加载类的需求时,先请求父加载器帮忙加载,直到传到顶层启动类加载器,父加载不了再由子加载器加载;
- 使用原因
为了避免重复加载,父加载器加载过了子加载器就没必要再加载了,否则我们可以随时使用自定义的类代替Java核心API中的类型,好阔怕
21. volatile关键字
- 使用volatile关键字修饰变量,会强制将修改值立即写入主存,当一个线程修改时,会导致其他线程工作内存中的变量缓存无效,其他线程再读取变量时会去主存读取;
- volatile 修饰的变量会禁止指令重排序。
与synchronized对比:
- volatile只能修饰变量。synchronized还可修饰方法;
- volatile只能保证数据的可见性,不能用来同步,多个线程并发访问volatile修饰的变量不会阻塞。synchronized不仅保证可见性,而且还保证原子性,多个线程争夺synchronized锁对象的时候,会出现阻塞。
22. synchronized与Lock的区别
- synchronized是java内置关键字,在JVM层面,而Lock是个java类;
- synchronized无法判断是否获取到锁,Lock可以判断是否获取到锁;
- synchronized会自动释放锁(执行完同步代码会释放锁 ;执行过程中发生异常会释放锁),Lock须在finally中使用unlock()方法手工释放锁,否则容易造成线程死锁;
- 用synchronized关键字的线程,如果一个线程获得了锁,其他线程等待。如果线程阻塞,其他线程会一直等待,而Lock锁就不一定会等待下去,如果尝试获取不到就结束了。
23. 深拷贝和浅拷贝
这个问题纠结了好久,在看原型模式之前终于要解决一下子了。上定义:
- 浅拷贝:浅拷贝是指在拷贝对象时,对于八大基本类型(byte,short,int,long,char,double,float,boolean)的变量重新复制一份,而对于引用类型的变量只是对直接引用进行拷贝,没有对直接引用指向的对象进行拷贝;
- 深拷贝:深拷贝是指在拷贝对象时,不仅把基本数据类型的变量会重新复制一份,同时会对引用指向的对象进行拷贝,注意拷贝的时候值也拷贝过来。
在 Java 中,所有的 Class 都继承自 Object ,而在 Object 中有一个clone()方法,它被声明为了 protected ,所以我们可以在其子类中使用它。它限制所有调用clone()方法的对象,都必须实现Cloneable接口,否者将抛出 CloneNotSupportedException 这个异常。先看浅拷贝,
private class Father implements Cloneable {
public String name;
public int age;
public Child child; @Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException ignore) {
e.printStace();
}
return null;
}
}
如果使用该方法去clone,克隆出的Father对象的hashcode跟被克隆的对象的hashcode不同,但成员变量child的hashcode是一样的,说明引用对象使用的是同一个,再看深拷贝,
private class Father implements Cloneable {
public String name;
public int age;
public Child child; @Override
public Object clone() {
try {
Father cloneFather =(Father)super.clone();
cloneFather.child = (Child)super.clone();
return cloneFather;
} catch (CloneNotSupportedException ignore) {
e.printStace();
}
return null;
}
}
private class Child implements Cloneable {
public String name;
public int age; @Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException ignore) {
e.printStace();
}
return null;
}
}
在克隆Father对象时,又对成员对象Child(里边的成员变量是基本类型)进行了克隆,这就是深拷贝,层级拷贝,直到基本类型,这样代码量不可估计,所以有了序列化拷贝的方式,就是把对象放到流里,再拿出来,前提是没有transient对象,对象以及对象内部所有引用到的对象都是可串行化的,简单地讲,序列化就是将对象写到流中,写到字节流里就等于复制了对象,原来的对象并没有动,然后写到另个内存地址中去。。
public Object deepClone()
{
//写入对象
ByteArrayOutoutStream bo=new ByteArrayOutputStream();
ObjectOutputStream oo=new ObjectOutputStream(bo);
oo.writeObject(this);
//读取对象
ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi=new ObjectInputStream(bi);
return(oi.readObject());
}
24. Java为什么取消多继承
子类继承多个父类有两个问题:
- 多个父类中有相同名字的变量,子类在调用的时候不知道调用哪一个;
- 多个父类中有相同名字的方法,子类未重写,子类在调用的时候不知道调用哪一个。
25. Java IO和装饰类模式
Java IO这块流比较多,以前也没看过,最近在看装饰者模式(https://www.cnblogs.com/lcmichelle/p/10854871.html),正好梳理了一下。Java应用程序通过输入流(InputStream)的Read方法从源地址处读取字节,然后通过输出流(OutputStream)的Write方法将流写入到目的地址。
首先,流的来源主要有五种:文件(File)、字节数组、StringBuffer、其他线程和序列化的对象,因此,Java中有FileInputStream处理文件,ByteArrayInputStream处理字节数组,StringBufferInputStream处理StringBuffer,PipedInput-Stream处理线程间的输入流,ObjectInputStream处理被序列化的对象。还有一个SequenceInputStre-am处理包裹有多种数据来源的业务。
后来,这些流在使用过程中出现了一些问题:FileInputStream读磁盘速度慢;读出的数据都是byte[]类型,需要用户自己转换成基本类型;从Stream读出来的数据无法推回去。针对这些问题,Java需要增加拥有缓存的BufferedInputStream,把byte转换成JAVA基本类型的DataInput-Stream和回写数据到stream的Push-backInput-Stream。直接增加类的话,排列组合形成的子类太多,因此,Java采用了装饰者模式。
如下图所示,最里边的实心是处理文件读取的FileInputStream,外面套一个BufferedInputStream的壳,那么这部分就是带buffer的FileInputStream。如果再套一个DataInputStream,那么就成了能输出int这样java 基本类型并且带buffer的FileInputStream。搭配由客户去决定,我们只需要提供套壳和最里面的实心InputStream(InputStream的6个孩子)。客户在搭配的时候必须有一个实心,否则就没有数据来源。 BufferedInputStream,DataInputStream,PushbackInputStream都继承自InputStream类,这样才能实现嵌套。这3个套娃壳有着共同的特点都是用来装饰,在他们上层在抽象一个FilterInputStream,让FilterInputStream继承自InputStream,以后所有的装饰类都从FilterInputStream继承。
26. Comparable和Comparator接口
相同点:
- 都是Java util下的两个接口,都是对自定义的数据结构比较大小,比如Person类。
不同点:
- Comparable是内部比较器,定义在类的内部,而Comparator是外部比较器,定义在类的外边,不修改实体类;
- Comparable需要实现compareTo()方法,传一个外部参数进行比对;Comparator接口需要实现compare()方法,对外部传入的两个类进行比较;
- 实现Comparator接口代码采用策略模式,可以定义某个类的多个比较器,在排序时根据实际场景自由调用,而Comparable接口实现后不方便改动,扩展性不好。
举例:
comparable
class Person implements Comparable<Person>{//该接口强制让人具有比较性
private String name;
private int age;
public Person(String name,int age) {
this.name = name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Person o) {
if(this.age>o.age){
return 1;
}else if(this.age<o.age){
return -1;
}else{
return this.name.compareTo(o.name);
}
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
import java.util.Iterator;
import java.util.TreeSet; public class Demo {
public static void main(String[] args) {
Person p0 = new Person("张三",3);
Person p = new Person("张三",1);
Person p1 = new Person("张三",2);
Person p2 = new Person("张四",2);
Person p3 = new Person("张四",2); TreeSet<Person> treeSet=new TreeSet<Person>();
treeSet.add(p0);
treeSet.add(p);
treeSet.add(p1);
treeSet.add(p2);
treeSet.add(p3); Iterator<Person> iterator = treeSet.iterator();
while(iterator.hasNext()){
Person next = iterator.next();
System.out.println(next);
}
}
}
comparator
class Com implements Comparator<Person>{//该接口强制让集合具有比较性
@Override
public int compare(Person o1, Person o2) {
if(o1.getAge()>o2.getAge()){
return 1;
}else if(o1.getAge()<o2.getAge()){
return -1;
}else{
return o1.getName().compareTo(o2.getName());
}
}
} class Person{
private String name;
private int age;
public Person(String name,int age) {
this.name = name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet; public class Demo {
public static void main(String[] args) {
Person p0 = new Person("张三",3);
Person p = new Person("张三",1);
Person p1 = new Person("张三",2);
Person p2 = new Person("张四",2);
Person p3 = new Person("张四",2); TreeSet<Person> treeSet=new TreeSet<>(new Com());
treeSet.add(p0);
treeSet.add(p);
treeSet.add(p1);
treeSet.add(p2);
treeSet.add(p3); Iterator<Person> iterator = treeSet.iterator();
while(iterator.hasNext()){
Person next = iterator.next();
System.out.println(next);
}
}
}
可以看到comparator方法中是将Com对象作为参数传给treeset等需要进行排序的集合容器的构造函数中了。
最后,如果实现类没有实现Comparable接口,又想对两个类进行比较(或者实现类实现了Comparable接口,但是对compareTo方法内的比较算法不满意),那可以实现Comparator接口,自定义一个比较器。
27. JVM调优总结 -Xms -Xmx -Xss -XX
Xms是指设定程序启动时占用内存大小,就是JVM默认堆的大小。一般来讲,大点,程序会启动的快一点,但是也可能会导致机器暂时间变慢。
Xmx是指设定程序运行期间最大可占用的内存大小。如果程序运行需要占用更多内存,超出了这个值,就会抛出OutOfMemory异常。
Xss是指设定每个线程的堆栈大小。这个就要依据你的程序,看一个线程大约需要占用多少内存,可能会有多少线程同时运行等。
-XX:PermSize用于设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。
上面四个参数的设置都是默认以Byte为单位的,也可以在数字后面添加[k/K]或者[m/M]来表示KB或者MB。而且,超过机器本身的内存大小也是不可以的,否则就等着机器变慢而不是程序变慢了。
28. 同步和异步
- 同步:发送一个请求,等待返回,然后再发送下一个请求,比如电话,需要接通对方
- 同步好处:按顺序修改可以避免出现死锁,读脏数据的发生
- 异步:发送一个请求,不等待返回,随时可以再发送下一个请求,比如广播
- 异步好处:多线程并发可以提高效率
29. 大端和小端
计算机的内存最小单位是是BYTE(字节)。
一个大于BYTE的数据类型在内存中存放的时候要有先后顺序。
高内存地址放整数的高位,低内存地址放整数的低位,这种方式叫倒着放,术语叫小端对齐。电脑X86和手机ARM都是小端对齐的。
高内存地址放整数的低位,低内存地址放整数的高位,这种方式叫正着放,术语叫大端对齐。很多Unix服务器的cpu都是大端对齐的。
30. Lambda表达式的意义
类似匿名类,具体参见:做了这么久的程序员,你知道为什么会有Lambda表达式吗?
理论篇-Java中一些零碎的知识点的更多相关文章
- 关于Java中的String类知识点小总结
Java中的String类知识点 前言 在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串. 如何创建字符串 最简单的方式 String str = "he ...
- 工具篇-Java中一些utils
下边是整理的一些Java开发的utils,顺便吐槽下新浪博客的编辑器排版跟我写的博客一样 烂,所以改用博客园 一.字符串 1. Java中String与其他类型之间的转换 String与日期对象 pu ...
- Java入门:零碎的知识点
实例变量经常被称为属性 成员变量和局部变量:前者在类中定义,后者在类的方法中定义且系统不会自动赋初始值 我们创建一个对象的时候实际上执行的是无参的构造方法 静态变量 static String arr ...
- 所有和Java中代理有关的知识点都在这了。
对于每一个Java开发来说,代理这个词或多或少都会听说过.你可能听到过的有代理模式.动态代理.反向代理等.那么,到底什么是代理,这么多代理又有什么区别呢.本文就来简要分析一下. 代理技术,其实不只是J ...
- java中的字符流知识点总结
java中字符流 字符流:对文本的读取,速度比字节流快 常见的字符流:Reader 和 Writer Reader是InputStreamReader的父类,InputStreamReader是Fil ...
- java中IO流相关知识点
(一) 下边使用outputStream字节输出流进行写操作 package zdbIO;import java.io.File;import java.io.FileNotFoundExceptio ...
- C#、Java中的一些小知识点总结(持续更新......)
前言:在项目中,有时候一些小的知识,总是容易让人忽略,但是这些功能加在项目中往往十分的有用,因此笔者在这里总结项目中遇到的一些实用的小知识点,以备用,并持续更新...... 1.禁用DataGridV ...
- 【java】一些零碎的知识点
java注释文档 一些常用的javadoc标签 常用javadoc标签 @see: other-class 引用other-class 生成的html文档会有一个See Alse 作为超链接的只是条目 ...
- Java中多线程并发体系知识点汇总
一.多线程 1.操作系统有两个容易混淆的概念,进程和线程. 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进程的地址空间是互相隔离的:进程拥有各种 ...
随机推荐
- rabbitmq.config配置参数详解
rabbitmq.config详细配置参数 详细使用方法请点击:http://www.cnblogs.com/wyt007/p/9073316.html Key Documentation tcp_l ...
- leetcode — path-sum
/** * Source : https://oj.leetcode.com/problems/path-sum/ * * * Given a binary tree and a sum, deter ...
- 开源 serverless 产品原理剖析 - Kubeless
背景 Serverless 架构的出现让开发者不用过多地考虑传统的服务器采购.硬件运维.网络拓扑.资源扩容等问题,可以将更多的精力放在业务的拓展和创新上. 随着 serverless 概念的深入人心, ...
- 为容器化的 Go 程序搭建 CI
本文介绍如何使用 Jenkins 的声明式 pipeline 为一个简单的 Golang web 应用搭建 CI 环境.如果你还不太了解 Jenkins 及其声明式 pipeline,请先参考笔者的 ...
- Mybatis配置信息浅析 MyBatis简介(二)
官方文档入门篇中有明确说明 每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的. SqlSessionFactory 的实例可以通过 SqlSessionF ...
- [十三]基础数据类型之AbstractStringBuilder
String内部是一个private final char value[]; 也就意味着每次调用的各种处理方法,返回的字符串都是一个新的,性能上,显然.... 所以,对于可变字符序列的需求是很明确 ...
- 【学习笔记】tensorflow基础
目录 认识Tensorflow Tensorflow特点 下载以及安装 Tensorflow初体验 Tensorflow进阶 图 op 会话 Feed操作 张量 变量 可视化学习Tensorboard ...
- 【Docker】基础学习及在.Net Core应用
一.Docker基础 Docker 是一个开源的应用容器引擎,基于 Go 语言 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然后发布到任何流行的 Linux 机器上 ...
- [转载] spring aop 环绕通知around和其他通知的区别
前言: spring 的环绕通知和前置通知,后置通知有着很大的区别,主要有两个重要的区别: 1) 目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知 是不能决定的,他们只 ...
- CA证书理解?CA证书的作用?
CA证书顾名思义就是由CA(Certification Authority)机构发布的数字证书.要对CA证书完全理解及其作用,首先要理解SSL.SSL(security sockets layer,安 ...