Java

java体系结构:
1.java程序设计语言
2.java API
3.编译器与工具包
3.java class文件
4.jvm
============================================================================================
java socket:
某语言将域名解析包含在里面有些语言需自己解析。
socket模拟http协议时,必须以两个回车换行\r\n\r\n结束,这是协议规定。
socket.shutdownInput(),socket.shutdownOutput()关闭输入输出流,而不关闭socket.
socket.close()关闭socket

============================================================================================
1,java命令:java jvmoptions(standard[-verbose,-Dkey=value,-classpath...] nostandard[-X] nostable[-XX]) com.venlv.Mainclass mainparams
          -X格式例如:-Xmx1024M
          -Dsysprop=syspropvalue
          -XX:+<option> 启用选项
          -XX:-<option> 不启用选项
          -XX:<option>=<number> 给选项设置一个数字类型值,可跟单位,例如 32k, 1024m, 2g
          -XX:<option>=<string> 给选项设置一个字符串值,例如-XX:HeapDumpPath=./dump.core
============================================================================================
2,栈和程序计数器的内存是线程私有的,当栈的深度大于虚拟机所允许深度会抛出StackOverflowError,当栈动态扩展申请失败时会抛出OutOfMemoryError,栈参数-Xss
============================================================================================
3,堆是线程共享的,参数-Xmx,-Xms,空间不足抛出OutOfMemoryError
============================================================================================
4,jvm中引用reference有两种实现:句柄(指针的指针)和指针,句柄就是引用不直接指向实例对象,而指向另一个指针,而另一个指针才指向实例对象,好处是,如果GC要移动对象时,只需改变另一个指针(这个指针在堆上句柄池中存储),而不用改栈中的引用值
============================================================================================
5, 垃圾回收基本概念:基本回收策略是标记-清理算法,在它上面改进出标记-复制算法,标记-整理算法以及分代收集算法。标记是看对象是否已死,它的算法有引用计数器算法和根搜索算法。
============================================================================================
6, sun的hotspot jvm对垃圾回收算法的具体实现:采用分代回收算法,分为新生代和老生代,新生代采用标记-复制算法,老生代采用标记-整理算法。对新生代回收也叫局部gc,老生代回收也叫full gc。新生代又分为eden和两个survivor区,其中新生代区比老生代区空间小 ,并且新生代采用复制收集算法而非标记-整理算法,所以速度是毫秒级别,但也与新生代大小有关,因为新生代采用复制算法所以清理时必须挂起所有用户线程,这样就会出现如果新生代空间过小会频繁回收而影响吞吐量,如果新生代空间太大会回收时间太长产生停顿而影响响应速度。新生代区中eden区比survivor区的空间要大,因为新生代区基本都是朝生暮死对象,所以回收时往survivor区只放入很少存活下来的对象。为什么有两个survivor区是因为如果新生代区的对象往旧生代移动有个计数器,只有超过计数阀值才移到旧生代,而这个计数过程是通过两个survivor的来回复制来实现。
============================================================================================
7, 继承和多态:Parent p= new Child(); p.parentMethod(); 对象和方法是动态绑定的,p对象为Child类型,所以parentMethod的第一个隐形参数this为Child类型的对象,它可以在父类方法中访问父类中的成员变量。并且如果权限够的话,也可以在子类方法访问protected的父类成员变量或在其它类方法中访问public级别的父类成员变量。
============================================================================================
8, 异常:是一种执行到某条指令而发生错误。不管什么类型的异常或错误,如果程序员不处理,只是往上层抛,最后到最顶层(run方法或main方法)会在当前线程终结不会影响其它线程,异常只会引起当前的线程终止。即使main线程终止也不会影响非主线程的运行。java提供了一种异常处理机制try...catch,在发生错误的地方try了,它会跳到catch里执行。异常分为Error和Exception两种,Exception又分RuntimeException和非RuntimeException。异常类设计原理:Error为系统级的异常,RuntimeException是逻辑异常,Error和RuntimeException属于UncheckedException,非RuntimeException是CheckedException。UncheckedException在异常发生时不用程序员捕获或显示抛出,CheckedException必须程序员捕获或显示抛出;这是因为程序员写的本程序也是一种资源,程序里使用if...else判断完全可以控制这些资源,判断是否为空,数组是否越界等,如果是对本程序资源处理不完善就会出现NullpointException等的RuntimeException也即是逻辑异常,这种异常是完全可以避免掉的;非RuntimeException是处理外界资源出现的异常情况,如FileNotFoundException,IoException等不属于逻辑必现异常;Error是虚拟机内部出现的异常,如OutOfMemoryError等。出于以上设计类的原因处理方式也不相同,Error归为UncheckedException是因为系统级的错误,程序本身处理不了也不必捕获;RuntimeException是逻辑不完美出现的异常,出现了完全可以消灭掉;非RuntimeException是外界资源不确定因素造成的,这种异常为什么让必须捕获,是因为程序员虽然处理不了,但可以记录日志或者关闭掉IO或文件这些资源。
============================================================================================
9, 多线程:临界资源就是共享资源,临界区就是操作共享资源的代码块,线程互斥是指临界区的串行化执行,线程同步是指临界资源的协同顺序执行,锁就是共享资源的属性,多个线程要想进入临界区必须先竞争获取锁,共享资源还有两个线程队列的属性,一个是就绪队列一个是阻塞队列。线程互斥是通过synchronized关键字实现,线程同步是通过wait/notify方法及synchronized关键字一起实现。
============================================================================================
10, ThreadLocal: 存储变量的容器,key为线程实例,值为要存储的变量。将该线程很多地方都想访问的对象保存在threadlocal中,并将threadlocal对象定义为静态的,这样可以解决减少参数传递。它和通过同步或互斥机制来保证访问同一个资源的完整性和正确性是两个不同的问题。
============================================================================================
11, 方法覆盖规则:(1)子类访问权限必须大于等于父类;(2)子类返回值必须与父类返回值类型相同或是其子类;(3)子类throws不能比父类多

============================================================================================
类加载器:
在jvm中唯一标识一个java.lang.Class实例的是classload instance+package+classname
类加载器分三类:启动类加载器<JAVA_HOME/lib>,扩展类加载器<JAVA_HOME/lib/ext>,appClassLoader<classpath>
加载一个类使用的类加载器与这个类的调用类使用相同的类加载器,而每个类加载器又使用的双亲委派规来加载来最终决定使用哪个类加载器。

例如: main方法里new String对象,String对象的使用哪个类加载器?
首先main方法所在的类使用的是app类加载器,所以String也使用app类加载,app类加载器使用双亲委派机制将给父加载器,最后交给根加载器即启动类加载器,启动类加载器看String属于<JAVA_HOME/lib>包,所以就将String类使用启动类加载器加载。



============================================================================================
map,set的散列值的获取:
准备知识:
==表示两个对象是否指向同一个内存地址。
equals的Object的默认实现是使用==
hashcode的值是内存地址转化对应来的一个整数值。由于整数不能完全对应内存空间,所以不同的对象可能有相同的hashcode,hashcode方法的引入基类中完全是为了提高散列效率。


进入正题 :
map.put(key,value)先根据key的hashcode找到内存地址,将value放在这个地址上,如果这个地址已经有其它value存在,则会将这个value以链表形式挂在最后。
使用map.get(key)获取值的过程是,先使用key的hashcode散列值取,如果只有一个值则直接返回,反之则使用equals方法来从链表中比对相等的object取出。

对象排序时使用Comparator ,只是实现equals方法。

============================================================================================

============================================================================================
Object的equals方法实现是比对==,表示类实例只与自身相等,即相同引用的对象相等。
equals的实现要遵守"等价关系":
自反性,对称性,传递性,一致性。
============================================================================================
StringBuffer线程安全,StringBuilder线程不安全
============================================================================================

lambda表达式:
只拥有一个方法的接口叫做函数式接口
编译器如何断定一个接口是否是函数式接口:
1,如果接口有注解@FunctionalInterface, 编译器会检查它是否满足函数式接口的定义
2,如果没有注解,编译器会查询该接口的方法是否只有一个,(剔除掉静态方法,默认方法或类似于toString的方法)

java8之前已有的函数式接口有Runable,Comparator...
java8引入的函数式接口:java.util.function
Predicate<T>谓词,判断真假;
Consumer<T>参数T;
Function<T,R>参数T,返回R;
Supplier<T>返回T;
UnaryOperator<T>一元操作,继承自Function<T,R>;
BinaryOperator<T>二元操作

引入lambda的目的,是替换匿名内部类为匿名方法。
用法: FileFilter fileFilter= (File f)->f.getName.endsWith("*.java");
  new Thread(()->{
//do something
});
函数式接口是lambda表达式的类型, 也可以说是lambda表达式的目标类型,
lambda表达式语句块内不仅可以访问局部变量,还能访问成员变量。

=======================================================================================================
java spi(service privider interface)
jdbc,common-logging都是基于spi的规范实现的,
例如jdbc对外接口由jdk制定,而具体的实现由数据库厂商,
这时,客户端使用用jdbc时是不调用具体厂商实现接口,而是调jdk的jdbc接口,所以具体厂商的实现必须注册到jdk提交的jdbc里,
有两种具体实现方式,1.客户端手动注册比如:Class.forName("com.mysql.jdbc.Driver"); 调用这个类之后,Driver的static方法块中含有代码是向jdbc的DriverManager中注册mysql的Driver
2.使用java.util.ServiceLoader.load(classname)
============================================================================================
Volatile两个作用:
保证内存可见性
防止指令重排
============================================================================================
开销对比
本地赋值 i=n 1.0
实例赋值 this.i=n 1.2
方法调用 funct() 5.9
新建对象 new Object() 980
新建数组 new Int[10] 3100
============================================================================================
java.util.ConcurrentModificationException是使用iterator.next()遍历list过程中删除list.remove(obj)其中的一个元素时抛异常,这是因为有两个维护list总数的状态不一致造成的,
解决办法是使用iterator.remove()来删除当前元素,这样它会同步更新索引的一致性,而不会出错。
对于map需要使用map.entryset.iterator.remove();
============================================================================================
java代理:是为了在具体实现中加入一些公用功能,比如记日志。
java静态代理:角色有Subject,realSubject,Proxy, 其中realSubject和Proxy都实现Subject接口,而proxy成员变量有suject的引用指向realSubject。
java动态代理:
基础知识:
动态代理工具包有:ASM,Javassist,CGLIB
它们的基本原理都是根据class文件的二进制编码规范在内存生成二进制形式,并通过自定义的类加载器加载生成动态对象,也可以写入文件生成.class文件。
ASM语法直接面向汇编指令的,格式如下:
       ClassWriter.visit(Opcodes.V1_7, // java版本   
               Opcodes.ACC_PUBLIC, // 类修饰符   
                 "ClassName" // 类的全限定名   
                 null "java/lang/Object" null ); 
//创建构造函数   
MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC,  "<init>" "()V" null null );  

Javassist语法相对高级点:
//创建Programmer类        
CtClass cc= pool.makeClass( "com.samples.Programmer" ); 
//定义code方法   
CtMethod method = CtNewMethod.make( "public void code(){}" , cc);  

JDK的动态代理就的底层框架就是使用Javassist, Javassist是Jboss的一个开源子项目。

静态代理的缺点是代理类太多了,例如如果有三个接口都想使用日志代理功能,那么针对每个接口都必须创建相应的代理。
JDK动态代理可以只使用一个动态代理来解决静态代理的缺陷,先介绍一个简化的实现:
Subject subject = Proxy.getInstance(ClassLoader, Interface){
//interface.method1的方法的开始处通过javassist修改字节码的方式加入日志功能
//interface.method1的方法的结束处通过javassist修改字节码的方式加入日志功能
//interface.method2的方法的开始处通过javassist修改字节码的方式加入日志功能
//interface.method2的方法的结束处通过javassist修改字节码的方式加入日志功能
};
subject.method1();
这种方式虽然只要是接口,不管是什么类型的接口都会通过修改字节码的方式来动态代理了,但是修改增加的日志也是业务功能却以这种方式增加,不够灵活 。

JDK的真正代理引入了 InvocationHandler 来解决增加的日志功能可以以java代码的方式来扩展,这个接口只有 invoke ( Object  proxy, Method  method, Object [] args)这个方法,具体方式是Proxy. newProxyInstance ( ClassLoader  loader, Class <?>[] interfaces, InvocationHandler  h); newProxyInstance将会以二进制的形式生成一个动态代理类,二进制里每个方法都会调用 InvocationHandler.invoke方法,
public  Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject){  
//方法前增加日志功能
paramMethod.invoke(InvocationHandler成员变量的realSubject,  null );  
//方法后增加日志功能 
 };
使用proxy.method1时,其实是调用了handlerImpl.invoke方法,invoke方法有日志功能和realSubject.method1

JDK的动态代理设计局限是只能代理接口,不能代理具体的实现类。
在面向对象的编程之中,如果我们想要约定Proxy 和RealSubject可以实现相同的功能,有两种方式:
      a.一个比较直观的方式,就是定义一个功能接口,然后让Proxy 和RealSubject来实现这个接口。
     b.还有比较隐晦的方式,就是通过继承。 因为如果Proxy 继承自RealSubject,这样Proxy则拥有了RealSubject的功能,Proxy还可以通过重写RealSubject中的方法,来实现多态。 其中JDK中提供的创建动态代理的机制,是以a这种思路设计的,而cglib 则是以b思路设计的。
/* 设置此系统属性,让JVM生成的Proxy类写入文件 */ System .setProperty ( "sun.misc.ProxyGenerator.saveGeneratedFiles" , "true" ) ;

CGLIB不仅可以生成二进制的class文件,而且可以与jdk自带的动态代理一样做代理,是一个框架,而而不像ASM和javassist的工具包。

CGLIB的主要角色有:Ehancer.create()生成具体类的代理子类, MethodInterceptor和 InvocationHandler 功能一样。
Enhancer enhancer =  new  Enhancer(); 
enhancer.setSuperclass(concreteClassInstance.getClass());
enhancer.setCallback(MethodInterceptorImpl);
ConcreteClass proxy =enhancer.create();


============================================================================================
java8新特性:
1.java程序设计语言
lamba表达式与Functional接口
default方法:仅能添加在interface里。
statict方法:允许在interface里定义静态方法。
interface引入default和static方法的目的是兼容老接口的同时增加新的功能。
方法引用:使语言结构更紧凑,减少冗余代码,格式有:Car::new(创建Car对象), Car::staticMethod, carObj::method
2.java API
Optional类
Stream框架
Date/Time API (JSR 310)
JavaScript引擎Nashorn API

3.编译器与工具包
Nashorn引擎: jjs
类依赖分析器jdeps

4.java class文件
5.jvm
PermGen空间被移除了,取而代之的是Metaspace(JEP 122)

============================================================================================
静态内部类与非静态内部类:
首先没有静态的外部类。
内部类:可以看作是外部类的成员变量,但比成员变量和成员方法的功能更强大, 静态内部类与非静内部类只是访问外部类的权限不一样。
非静态内部类能够访问外部类的静态和非静态成员。静态类不能访问外部类的非静态成员。他只能访问外部类的静态成员。
一个非静态内部类不能脱离外部类实体被创建:
OuterClass.NestedStaticClass printer = new OuterClass.NestedStaticClass();
OuterClass.InnerClass innerObject = new OuterClass().new InnerClass();
========================================================================================================
绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来 。对java来说,绑定分为静态绑定和动态绑定;或者叫做前期绑定和后期绑定.
java当中的方法只有final,static,private和构造方法是前期绑定
后期绑定: 在运行时根据具体对象的类型进行绑定
============================================================================================
并发:
Thread的run方法和start方法的区别:
start方法是将当前线程设置为就绪状态,等时间片轮换机制到的时候会自动执行run方法。
run方法是普通方法,直接调用不会新启线程,而是在main线程中执行。

每个对象和类都只有一个锁,进入synchronize块内之后就获取了锁,其它线程不能进入,但可临界区以外的地方还是可以访问带锁对象。
如果有多个synchronize块,只要进入一个synchronze块内,即获得了锁,其它sychronize也会被block。


============================================================================================
静态方法不能重写。
Class A{public static show(){print("a");}}
Class B extends A{public static show(){print("b");}}
A a = new B(); a.show(); //结果是a
B b = new B(); b.show(); //结果是b
============================================================================================
Optional的正确使用方式:
消除为空判断。
创建Optional对象: Optional.of(obj) ,   Optional.ofNullable(obj)  和  Optional.empty()
使用Optional对象:
user.orElse( null ) user为空时返回null
user.orElseGet(Supplier )user为空时返回supplier创建的对象
user.ifPresent(Consumer );如果不为空则Consumer
user. map (u -> u.getOrders()).orElse( Collections .emptyList())map返回的是Optional所以可以级联调用。
user .map( u - > u .getUsername()).map( name - > name .toUpperCase()) .orElse( null );
============================================================================================
java热替换:
1. bootstraploader, extloader, apploader加载流程:
loadClass(String fullClassName)先findLoadedClass去jvm看是否加载过,加载过直接返回,没加载过走双亲委派逻辑,找到真正的classloader,该classloader会调用findClass,findclass先找到class文件,然后生成二进制数据,使用该数据调用defineClass去在jvm中生成class对象。

首先一个类如果被一个类加器加载过,它是不能被覆盖加载的,会抛错attempted duplicate class definition for name: "com/lewis/mockit/ConcreteSubject"。所以热替换的原理是每次重新定义类加载器来加载类。
============================================================================================
int的包装类Integer的值在-128与127之间的对象都会放在IntegerCache.cache中缓存,但其它的数据都会在堆中新产生。
所以包装类的比较一定要使用equals方法,而不能直接用等号比较。
============================================================================================
底层原理: JVM源码分析之javaagent原理完全解读 http://lovestblog.cn/blog/2015/09/14/javaagent/
============================================================================================
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值