后台开发的过程中积累的关于java的杂记

架构

SSH框架

为什么要分层?

因为分层使代码变得清晰,容易写也容易阅读,更重要的是让代码扩展性更好,层与层之间的改动不会互相影响

各层的分工

  1. dao——与数据库交互
  2. service——处理业务逻辑,调用dao层方法
  3. action——用来控制转发,接到请求交给service处理

dao是用于操作数据用的,service是为页面功能服务的,在service中对数据进行处理计算,然后返回数据结果到ACTION,而action则再对数据进一步处理,比如把list转成json,把两个service数据进行合并等,并发送到jsp页面显示。

并发相关

ReentrantLock

参考Java多线程11:ReentrantLock的使用和Condition

  1. lock之后要自己unlock
  2. lock相比synchronized更加灵活,可以通过trylock判断锁是不是被占用了,在被占用的情况下可以忙其他事,而不是直接就阻塞了
  3. lock持有的是对象监视器,也就是类似于syncronized(this){} ,但是需要注意这两者持有的对象监视器是不同的
  4. lock配置Condition的signal和await可以实现syncronized的wait和notify来实现等待/通知模型,相比之下Condition更灵活:一个lock实例可以创建多个Condition实例,实现多路通知和有选择性的通知,而不是像notify一样是由jvm随机选择的

BlockingQueue

概念

阻塞队列是一种支持当获取元素时会阻塞直到队列不为空,当插入元素时阻塞直到队列有空间。

方法

操作阻塞队列有四种形式的方法,add/remove/element抛出异常,offer/poll/peek返回具体值,put/take阻塞,offer(e, time, unit)、poll(time, unit)指定等待的最长时间

不同实现

  1. ArrayBlockingQueue 只有一个锁,通过两个condition来实现阻塞、通知,添加和删除数据时只允许一个被执行
  2. LinkedBlockingQueue 有两个锁,putLock和takeLock,各自维护一个condition,添加和删除数据可以允许并行,当然删除和添加最多各自有一个线程在执行。
  3. LinkedBlockingQueue 不仅在消费数据的时候进行唤醒插入阻塞的线程,而且在插入如果容量还没满,也会唤醒插入阻塞的线程

jvm原理

垃圾回收

参考深入理解java垃圾回收机制

垃圾回收就是java中的一个亮点(有利有弊),通过一定的算法自动管理对象的生命周期,防止内存泄漏(内存对象的生命周期超过了程序需要它的时长)

垃圾回收的算法有:

  1. 引用计数:早期的算法,通过给堆中的每个对象内置一个引用计数器来实现(缺点是:无法检测循环引用)
  2. 标志、清洗算法
  3. 分代收集:频繁收集新生代,比较少的收集老生代,基本不收集持久代(分代回收的GC分为 minor gc 和 full/major gc,以下为两种gc的日志格式)
  • GC:
  • FULL GC:
  • 新的对象都在eden区上创建,当eden区的大小达到阈值就会发生GC,eden区中存活的对象会复制到survivor区,并清除eden中无效的对象,如果survivor区中的对象达到年龄限制或者大小达到阈值,就会将存活的对象复制到old区,如果这时old区空间不足就会发生full gc,full gc之后old区的空间仍然无法承载young区要晋升的对象大小,就会发生OOM

内存分配

参考Java里的堆(heap)栈(stack)和方法区(method)深入探究JVM | 探秘Metaspace

内存分为 heap、stack、method

heap:

  1. 堆存放的都是对象,空间大但是访问慢(时空守恒)
  2. 为所有线程所共享
  3. java heap主要分成三种:
  • young:主要用来存放新生的对象
  • old: 主要用来存放生命周期长的内存对象
  • permanent:主要用来存放类和方法的元数据信息和常量池 ,类被加载后就放入这个区域。GC不会对持久层进行清理,
  • metaspace:在java8中持久代已经被移除了,因为持久代的大小是固定的,所以在类加载很多的情况下,容易出现OOM:PermGen space错误,类和方法的元数据被移入元数据区。(存在于本地内存中,所以大小只受物理内存的影响)元数据区是自动增长的,通过-XX:MaxMetaSpaceSize来限制Metaspace的大小,以前的Perm参数失效,超过最大值将会在metaspace发生full gc收集dead class或者classloader

stack:

  1. 栈区放的都是基础数据类型和对象的引用
  2. 保存函数调用的现场

method:

  1. 方法区也为所有线程所共享(另一种说法也就是permanent区)
  2. 方法区存放的都是在整个程序中永远唯一的元素,包括class、static变量
  3. 常量池也是方法区的一部分,存放程序中的字面量如”hello“ 以及常量

java 集合框架

哈希结构

哈希表的数组长度为什么总是习惯用2^n?

hash 的时候总是需要对对象的hashCode取哈希表长度的模,对于2^n 取模,可以简化为 hash & (2^n - 1),提高效率

jdk1.8中的hashmap中的 hash算法是什么?有什么优点?

hash算法是(h = key.hashCode()) ^ (h >>> 16) ,通过这样hash,高位的变化反映到低位里,这样我们取模的时候hash & (length - 1) 就不会只取低位相关,防止有些hashCode只和高位相关造成的冲突过多

hashtable、hashmap、concurrenthashmap 哈希家族的异同点

  1. 都是继承于map接口,用于存储键值对。hashtable是同步的,如果不需要线程安全,推荐使用hashmap代替hashtable,如果需要高并发线程安全的实现,使用ConcurrentHashMap代替hashtable
  2. 集合方法返回的iterators是“fail-fast”的,也就是说当hash结果被修改,除了通过iterator的remove方法外的改动,都会造成iterator抛出ConcurrentModificationException异常。因此,在并发修改的情况下,iterator很快失败并清除,而不是冒险在未来不确定的时间做不确定的事。hashtable的方法返回的emurations却不是fail-fast的
  3. hashmap可以接受null值,hashtable则不行
  4. HashMap可以通过下面的语句进行同步:Map m = Collections.synchronizeMap(hashMap);

ConcurrentHashMap(面试必考)

concurrentHashMap是一个并发的hash表的实现,它支持完全的并发读取,支持大数量的并发更新操作。

高性能的原因:

  1. 用分离锁实现多个线程间的更深层次的共享访问,不再是只有一个线程能同时持有容器的锁了。
  2. 利用hashEntry的不变性(final hash,key,next)来降低读操作对加锁的需求
  3. 用volatile变量协调读写线程间的内存可见性

缺点

  1. 返回的迭代器是弱一致性的,fail-safe并且不会抛出ConcurrentModificationException异常

源码分析(基于jdk1.7)



concurrentHashMap是由segment数组和hashEntry数组组成的,segment是一种可重入锁ReentranLock,在CHM中扮演锁的角色,HashEntry用于存储键值对数据。一个CHM中包含一个segment数组,segment的结构和hashmap类似,一个segment中包含一个hashEntry数组,每个HashEntry都是一个链表的结构,每个segment守护着自己的hashEntry数组,要往这一段hashEntry数组中修改,必须先获得相应的锁

arrayList、linklist、vector

  1. arrayList 内部用数组实现,随机访问和遍历快,插入删除慢
  2. linklist 内部用链表实现,适合数据的动态插入和删除,随机访问和遍历慢
  3. vector 跟 arraylist差不多,除了以下几点
    • vector是线程安全的,因此访问速度也较慢
    • arraylist在内存不够时扩展50% + 1个,vector默认扩展一倍

java 代码执行顺序

JAVA类首次装入时,会对静态成员变量或方法进行一次初始化,但方法不被调用是不会执行的,静态成员变量和静态初始化块级别相同,非静态成员变量和非静态初始化块级别相同。

初始化顺序:先初始化父类的静态代码--->初始化子类的静态代码-->(创建实例时,如果不创建实例,则后面的不执行)初始化父类的非静态代码(变量定义等)--->初始化父类构造函数--->初始化子类非静态代码(变量定义等)--->初始化子类构造函数

tips:

若子类没有显示调用父类的构造函数,则默认调用父类的无参构造函数,如果父类没有则编译错误

java 命令行参数

  1. -classpath

    java 通过指定-classpath 来指定虚拟机搜索的你要运行的类的目录、jar文件名、zip文件名,之间用;(linux 用:)隔开。否则java查不到你的class文件就会报java.lang.NoClassDefFoundError异常,在运行时可以通过System.getProperty(“java.class.path”)得到jvm查找类的路径

    也可以通过CLASSPATH环境变量来指定类搜索路径,建议用-cp

  2. -DpropertyName=value

    系统属性,可以通过System.getProperty(propertyName)获取value的值,用来设置全局变量值,如配置文件路径

  3. -Xms -Xmx 堆的最大最小值

  4. -Xss 线程堆的最大值

  5. --XX:+HeapDumpOnOutOfMemoryError

    当JVM不断地抛出OutOfMemory错误的时候,该命令会通知JVM拍摄一个“堆转储快照”, 并通过-XX:HeapDumpPath 指定该文件的保存路径,可以方便调试问题

  6. -XX:+UseParNewGC 使用多线程并发处理新生代GC

  7. -XX:+UseConcMarkSweepGC 使用CMS并发处理GC

  8. -Djava.awt.headless=true 无头模式,系统的配置模式,在该模式下,系统缺少了显示、键盘或鼠标。据说

在Java服务器程序需要进行部分图像处理功能时,建议将程序运行模式设置为headless,这样有助于服务器端有效控制程序运行状态和内存使用(可防止在处理大图片时发生内存溢出)

泛型

  • 上界:表示对泛型的限制,传进来的对象必须是class 或者 class 的子类
  • 通配符
    • java5之后添加了通配符 和 泛型(泛型我们是了解的),通配符的基本用法
		GenericType<?>
GenericType<? extends upperBoundType> // 设置上界
GenericType<? super lowerBoundType> // 设置下界
- 看了下 在 [Java 的泛型类型中使用通配符 - 博客频道 - CSDN.NET](https://app.yinxiang.com/shard/s15/nl/2659954/653fb6e2-ea9d-48f2-b4cf-5d5f749923fb/),觉得通配符主要是方便了**泛型对象作为方法参数可以引用泛型子类**,代码如下:
	List<? extends Number> nums = new ArrayList<>();
List<Integer> ints = Arrays.asList(1, 2);
List<Double> doubles = Arrays.asList(1.1, 2.2);
nums.addAll(ints);
nums.addAll(dbls); // addAll 方法使用了通配符作为参数
}
 // print all Number 或 Number 子类的list, 如果没有通配符的话, 估计得一个一个子类都去实现以下。。。
public void print(List<? extends Number> list) {
list.forEach(x -> System.out.println(x));
}
- 通配符的限制:
- 不能用来直接创建变量对象
- 不能进行修改操作,例如下面的代码,编译器可能觉得,鬼知道你引用了哪个子类呢
	List<? extends Number> nums = new ArrayList<Integer>();
nums.add(1); // 编译错误

java 基础概念

  • 守护线程: 只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作。当最后一个守护线程结束时,守护线程随同JVM一起结束工作。最典型的例子就是GC
  • volatile:
    • 用在多线程中,同步变量。一般情况下线程为了提高效率,会缓存主内存中的变量在自己的线程栈中,volatile声明的变量则不能缓存,保证了线程之间的变量一致性[虽然自己测不出这个效果。。。]
    • 不能保证线程安全。引用例子如下

假如线程1,线程2 在进行read,load 操作中,发现主内存中count的值都是5,那么都会加载这个最新的值,在线程1堆count进行修改之后,会write到主内存中,主内存中的count变量就会变为6;线程2由于已经进行read,load操作,在进行运算之后,也会更新主内存count的变量值为6;导致两个线程及时用volatile关键字修改之后,还是会存在并发的情况。

  • 创建对象:

创建对象有哪些方法?

  • 最普通的new(给对象分配空间,调用构造函数初始化,返回引用)

  • 调用对象的clone方法

    • clone给对象分配空间后,直接在内存上对已有对象影印,不需要构造函数
    • 需要实现CloneAble接口才能调用对象的clone方法,clone是一种浅复制,例如对象中包含一个String,那么新的对象中的String 跟原来的指向同一个字符串。
    • 要实现深复制,需要实现clone方法,不仅clone本身,还需要包含的引用对象
  • 运用反序列化手段,调用java.io.ObjectInputStream对象的 readObject()方法。【没玩过】

  • 访问修饰符: 只有private在同一个包内不能访问,其他包的只能public能访问

日志操作

log4j

log4j 使用写日志变得很简单,支持多个输出和格式化

log4j 主要由三个部分组成 logger 、appender、 layout

  • logger 负责日志的收集,主要由rootlogger 和 其他各个类的logger组成,通过logger.info等方法来记录消息,子logger中可以自定义各种配置,如果没有设置就会向上级的logger查找相应的配置,最上层为rootlogger
  • appender负责日志的输出,可以输出到文件、控制台、数据库、kafka等等
  • layout绑定到相应的的appender来格式化它的日志输出格式,丰满多姿

java 随记的更多相关文章

  1. Java入门记(五):容器关系的梳理(下)——Map

    注意:阅读本文及相关源码时,需要数据结构相关知识,包括:哈希表.链表.红黑树. Map是将键(key)映射到值(value)的对象.不同的映射不能包含相同的键:每个键最多只能映射到一个值.下图是常见M ...

  2. Java入门记(四):容器关系的梳理(上)——Collection

    目录 一.Collection及子类/接口容器继承关系 二.List 2.1 ArrayList 2.1.1 序列化的探讨 2.1.2 删除元素 2.1.3 调整大小 2.2 Vector和Stack ...

  3. Java入门记(三):初始化顺序

    初始化顺序的规则 1.在一个类的对象实例化时,成员变量首先初始化,然后才调用构造器,无论书写顺序.如果调用构造器前,没有显式初始化,那么会赋默认值. 这样做法的原因可以理解为:构造器执行时可能会用到一 ...

  4. Java入门记(二):向上转型与向下转型

    在对Java学习的过程中,对于转型这种操作比较迷茫,特总结出了此文.例子参考了<Java编程思想>. 目录 几个同义词 向上转型与向下转型 例一:向上转型,调用指定的父类方法 例二:向上转 ...

  5. Java入门记(一):折腾HelloWorld

    HelloWorld,学习每门语言的第一步.有人戏称,这些年的编程生涯就是学习各种语言的HelloWorld,不知是自谦还是自嘲.目前所在的公司使用Java作为主要开发语言,我进行语言转换也大半年了, ...

  6. Java琐记

    svn项目倒入,所选的文件夹一定是src上面以及的:然后eclipse会自动创建一个项目,项目名称就是src上级文件夹的名称:然后会按照路径下的文档结构如导入到eclipse的结构中: 被标记为// ...

  7. java随记2

    1.Arrays java8里新添加了parallelSort等parallel开头的方法,表示利用cpu并行的能力 2.面向对象 如果继承树里的某个类要被初始化时,系统将会同时初始化该类的所有父类 ...

  8. java随记 2月16

    1.a=a+b 等于 a+=b ,且a+=b隐含强制类型转换 2.^ 表示异或   两个二进制同号为假,异号为真    即 0^0=0,1^1=0,0^1=1 3.三元运算    布尔表达式 ?表达式 ...

  9. java面试记很多次还是记不住的问题

    1.java底层如何实现多态 https://blog.csdn.net/fan2012huan/article/details/51007517 (1)在常量池中找到方法调用的符号引用 (2)查看P ...

随机推荐

  1. 为异常处理做准备,熟悉一下WinDbg工具

    为异常处理做准备,熟悉一下WinDbg工具 马上开始异常处理第二讲,但是在讲解之前,还有熟悉一下我们的WinDbg工具,当然你如果熟悉这个工具,那么就可以不用看了. 一丶熟悉WinDbg界面 刚开始打 ...

  2. 这家IT教育公司太拼了:毕业生找不到工作就全额退学费!

    乐橙谷为了让更多的学生有工作,有高薪工作,已经决定尝试一种更刺激的游戏规则:完成课时的学员如果毕业找不到工作,公司将全额退还学费.这家公司秉承着自己的使命:以尊重的文化,敬畏的心态去努力帮助每个学生实 ...

  3. 全面了解Android热修复技术

    WeTest 导读 本文探讨了Android热修复技术的发展脉络,现状及其未来. 热修复技术概述 热修复技术在近年来飞速发展,尤其是在InstantRun方案推出之后,各种热修复技术竞相涌现.国内大部 ...

  4. 点击截图功能 js canvas

    使用:html2canvas实现浏览器截图 <html> <head> <meta name="layout" content="main& ...

  5. 关于API,前后端分离

    之前再开放新型web项目和app时,遇到了和前后端交互的问题.总所周知的是,web前后端交接时,最重要的交互方式的接口的制定. 而关于接口的规定,衍生出了一大堆问题,第一是关于空值的制定,是不输出呢? ...

  6. SQL Server 2016 Alwayson新增功能

    标签:SQL SERVER/MSSQL SERVER/数据库/DBA/ 概述 SQLServer2016发布版本到现在已有一年多的时间了,目前最新的稳定版本是SP1版本.接下来就开看看2016在Alw ...

  7. BCB中AnsiString类方法小结

    AnsiString类是BCB中最常见类之一,了解它对以后深入学习BCB大有帮助. 介绍AnsiString类之前,先要介绍一些背景知识.VCL(Visual Component Library 可视 ...

  8. Javascript 中 ==(相等运算符) 和 ===(严格相等运算符) 区别

    在JS中,"==="叫做严格运算符,"=="叫做相等运算符. 它们的区别是相等运算符(==)比较两个值是否相等,严格相等运算符(===)比较它们是否为" ...

  9. Reverse Words in a String III

    Given a string, you need to reverse the order of characters in each word within a sentence while sti ...

  10. margin:0px auto和text-align:center区别

    (1)margin:0px auto :作用于块级元素,对块级元素进行居中 (2)text-align:center:作用于内联元素,必须放在要居中的内联元素所在的块级元素. 例: (1) <d ...