面向对象:
面向对象:将问题分解成一个一个步骤,对每个步骤进行相应的抽象,形成对象,通过不同对象之间的调用,组合解决问题。占用资源相对高,速度相对慢
面向过程:自顶而下的编程模式.,把问题分解成一个一个步骤,每个步骤用函数实现,依次调用即可。占用资源相对低,速度相对快
面向对象特征:
- 继承:子类或派生类无需重新编写原来的类的情况下对这些功能进行扩展,实现方式有二类:实现继承与接口继承
- 多态:指一个类实例的相同方法在不同情形有不同表现形式,同一操作作用于不同对象,产生不同执行结果。
严格意义:运行期的动态绑定,满足三个条件:类继承或者接口实现、子类要重写父类的方法、父类的引用指向子类的对象
也有提静态多态:编译期确定,方法重载。
- 封装:客观事物封装成抽象的类,对象对内部数据提供了不同级别的保护,类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
重载 VS 重写:
重载:编译器绑定,有同样的名称,但是参数列表不相同的情形,其他可同可不同,访问修饰符可变,异常抛出可更广泛
重写:运行期绑定,在Java的子类与父类中有两个名称、参数列表、返回类型都相同的方法,访问级别的限制性弱,异常抛出更少或更有限的异常
java继承与实现:
继承:如果多个类的某个部分的功能相同,那么可以抽象出一个类出来,把他们的相同部分都放到父类里,让他们都继承这个类;extends,为了复用;可以定义属性方法,变量,常量
实现:如果多个类处理的目标是一样的,但是处理的方法方式不同,定义一个接口标准;implements;定义全局常量(static final)和无实现的方法(Java 8以后可以有defult方法)
Java的继承与组合:
继承:编译器,is-a,优点:子类能自动继承父类的接口,缺点:子类与父类紧密耦合,缺乏独立性
组合:运行期,has-a,优点:低耦合具有较好的可扩展性,缺点:整体类不能自动获得和局部类同样的接口
组合优于继承
java变量:
三种变量,分别是类变量、成员变量和局部变量。他们分别存放在JVM的方法区、堆内存和栈内存中
作用域:
对于成员变量和方法的作用域,public,protected,private以及不写之间的区别:
public
: 表明该成员变量或者方法是对所有类或者对象都是可见的,所有类或者对象都可以直接访问
private
: 表明该成员变量或者方法是私有的,只有当前类对其具有访问权限,除此之外其他类或者对象都没有访问权限.子类也没有访问权限.
protected
: 表明成员变量或者方法对类自身,与同在一个包中的其他类可见,其他包下的类不可访问,除非是他的子类
default
: 表明该成员变量或者方法只有自己和其位于同一个包的内可见,其他包内的类不能访问,即便是它的子类
平台无关性的实现:
前端编译:javac ,.java
代码转换成.class
代码
后端编译: 主要是将中间代码再翻译成机器语言。Java中,这一步骤就是Java虚拟机来执行的。
对于Java的平台无关性的支持是分布在整个Java体系结构中的。其中扮演着重要角色的有Java语言规范、Class文件、Java虚拟机等。
- Java语言规范
- 通过规定Java语言中基本数据类型的取值范围和行为
- Class文件
- 所有Java文件要编译成统一的Class文件
- Java虚拟机
- 通过Java虚拟机将Class文件转成对应平台的二进制文件等
Java的平台无关性是建立在Java虚拟机的平台有关性基础之上的,是因为Java虚拟机屏蔽了底层操作系统和硬件的差异。
jvm支持:如Kotlin、Groovy、JRuby、Jython、Scala等。之所以可以支持,就是因为这些语言也可以被编译成字节码。而虚拟机并不关心字节码是有哪种语言编译而来的。
值传递、引用传递:
- 值传递(pass by value)是指在调用函数时将实际参数
复制
一份传递到函数中,这样在函数中如果对参数
进行修改,将不会影响到实际参数。 - 引用传递(pass by reference)是指在调用函数时将实际参数的地址
直接
传递到函数中,那么在函数中对参数
所进行的修改,将影响到实际参数。
Java中的求值策略是共享对象传递,只有值传递,将对象的引用当作值传递
基本数据类型和包装类:
包装类均位于java.lang包,包装类和基本数据类型的对应关系如下表所示
基本数据类型 | 包装类 |
---|---|
byte | Byte |
boolean | Boolean |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
基本类型好处: 存在栈内存中,高效
包装类型:new
一个对象是存储在堆里的,我们通过栈中的引用来使用这些对象;所以,对象本身来说是比较消耗资源的。
包装类用途:1.集合容器要求object 2.基本类型具有了对象的性质,属性和方法
自动拆装箱:
Integer i =10; //自动装箱,装箱方法: Integer integer=Integer.valueOf(1);
int b= i; //自动拆箱,实现 : int i=integer.intValue();
自动拆装箱场景:
自动拆装箱与缓存机制:
整型:如果数字在-128至127之间时,会直接使用缓存中的对象,而不是重新创建一个对象。Java 5中引入的时候,范围是固定的-128 至 +127。后来在Java 6中,可以通过java.lang.Integer.IntegerCache.high
设置最大值。
有ByteCache用于缓存Byte对象
有ShortCache用于缓存Short对象
有LongCache用于缓存Long对象
有CharacterCache用于缓存Character对象
Byte
, Short
, Long
有固定范围: -128 到 127。对于Character
, 范围是 0 到 127。除了Integer
以外,这个范围都不能改变。
自动拆装箱带来的问题
当然,自动拆装箱是一个很好的功能,大大节省了开发人员的精力,不再需要关心到底什么时候需要拆装箱。但是,他也会引入一些问题。
包装对象的数值比较,不能简单的使用
==
,虽然-128到127之间的数字可以,但是这个范围之外还是需要使用equals
比较。前面提到,有些场景会进行自动拆装箱,同时也说过,由于自动拆箱,如果包装类对象为null,那么自动拆箱时就有可能抛出NPE。
如果一个for循环中有大量拆装箱操作,会浪费很多资源。
如何正确定义接口的返回值(boolean/Boolean)类型及命名(success/isSuccess)
- 基本类型自动生成的getter和setter方法,名称都是
isXXX()
和setXXX()
形式的。 - 包装类型自动生成的getter和setter方法,名称都是
getXXX()
和setXXX()
形式的。
1.在定义POJO中的布尔类型的变量时,不要使用isSuccess这种形式,而要直接使用success!否则引起序列化错误 Boolean success
2.建议在POJO类属性、和RPC的返回值和参数中使用包装类型
3.局部变量基本类型
String类型:
一旦一个string对象在内存(堆)中被创建出来,他就无法被修改。特别要注意的是,String类的所有方法都没有改变字符串本身的值,都是返回了一个新的对象。
如果你需要一个可修改的字符串,应该使用StringBuffer 或者 StringBuilder。否则会有大量时间浪费在垃圾回收上,因为每次试图修改都有新的string对象被创建出来。
substring(int begin,int end) java 6 和 7 实现不同:
java6:引起问题:
有一个很长很长的字符串,但是当你使用substring进行切割的时候你只需要很短的一段。这可能导致性能问题,因为你需要的只是一小段字符序列,但是你却引用了整个字符串(因为这个非常长的字符数组一直在被引用,所以无法被回收,就可能导致内存泄露)。在JDK 6中,一般用以下方式来解决该问题,原理其实就是生成一个新的字符串并引用他。
x = x.substring(x, y) + ""
JDK 7 中的substring
上面提到的问题,在jdk 7中得到解决。在jdk 7 中,substring方法会在堆内存中创建一个新的数组。
replaceFirst、replaceAll、replace区别
- replace、replaceAll和replaceFirst是Java中常用的替换字符的方法,它们的方法定义是:
- replace(CharSequence target, CharSequence replacement) ,用replacement替换所有的target,两个参数都是字符串。
- replaceAll(String regex, String replacement) ,用replacement替换所有的regex匹配项,regex很明显是个正则表达式,replacement是字符串。
- replaceFirst(String regex, String replacement) ,基本和replaceAll相同,区别是只替换第一个匹配项。
String对“+”的重载
-
String s = "a" + "b",编译器会进行常量折叠(因为两个都是编译期常量,编译期可知),即变成 String s = "ab"
-
对于能够进行优化的(String s = "a" + 变量 等)用 StringBuilder 的 append() 方法替代,最后调用 toString() 方法 (底层就是一个 new String())
-
字符串拼接方法:
有五种,分别是使用+
、使用concat
、使用StringBuilder
、使用StringBuffer
以及使用StringUtils.join
。用时比较StringBuilder
<StringBuffer
<concat
<+
<StringUtils.join,StringUtils.join更擅长处理字符串数组或者列表的拼接
+:实现原理是StringBuilder.append
。
concat:首先创建了一个字符数组,长度是已有字符串和待拼接字符串的长度之和,再把两个字符串的值复制到新的字符数组中,并使用这个字符数组创建一个新的String对象并返回,实际是new了一个新的String
append:
stringbuilder:与string不同,char[] 数组不是final的,可变长,append会直接拷贝字符到内部的字符数组中,如果字符数组长度不够,会进行扩展。
stringbuffer: append是synchronized
StringUtils.join
的也是通过StringBuilder
来
因此,经过对比,我们发现,直接使用StringBuilder
的方式是效率最高的。因为StringBuilder
天生就是设计来定义可变字符串和字符串的变化操作的。
但是,还要强调的是:
1、如果不是在循环体中进行字符串拼接的话,直接使用+
就好了。
2、如果在并发场景中进行字符串拼接的话,要使用StringBuffer
来代替StringBuilder
。
3、由于字符串拼接过程中会创建新的对象,所以如果要在一个循环体中进行字符串拼接,就要考虑内存问题和效率问题。不能用+,用builder
String.valueOf和Integer.toString的区别
我们有三种方式将一个int类型的变量变成呢过String类型,那么他们有什么区别?
1.int i = 5;
2.String i1 = "" + i;
3.String i2 = String.valueOf(i);
4.String i3 = Integer.toString(i);
第三行和第四行没有任何区别,因为String.valueOf(i)也是调用Integer.toString(i)来实现的。
第二行代码其实是String i1 = (new StringBuilder()).append(i).toString();,首先创建一个StringBuilder对象,然后再调用append方法,再调用toString方法。
Class常量池
- java文件----javac编译----》class文件,class文件 组成。
- class文件---javap -v----》查看常量池内容
Class常量池可以理解为是Class文件中的资源仓库。 Class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。
由于不同的Class文件中包含的常量的个数是不固定的,所以在Class文件的常量池入口处会设置两个字节的常量池容量计数器,记录了常量池中常量的个数。
字面量:在计算机科学中,字面量(literal)是用于表达源代码中一个固定值的表示法(notation)。几乎所有计算机编程语言都具有对基本值的字面量表示,诸如:整数、浮点数以及字符串;而有很多也对布尔类型和字符类型的值也支持字面量表示;还有一些甚至对枚举类型的元素以及像数组、记录和对象等复合类型的值也支持字面量表示法。
符号引用:相对于直接引用来说的。主要包括了以下三类常量: * 类和接口的全限定名 * 字段的名称和描述符 * 方法的名称和描述符
在《深入理解Java虚拟》中有这样的表述:
Java代码在进行
Javac
编译的时候,并不像C和C++那样有“连接”这一步骤,而是在虚拟机加载Class文件的时候进行动态连接。也就是说,在Class文件中不会保存各个方法、字段的最终内存布局信息,因此这些字段、方法的符号引用不经过运行期转换的话无法得到真正的内存入口地址,也就无法直接被虚拟机使用。当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址之中。
运行时常量池
运行时常量池( Runtime Constant Pool)是每一个类或接口的常量池( Constant_Pool)的运行时表示形式。
它包括了若干种不同的常量:从编译期可知的数值字面量到必须运行期解析后才能获得的方法或字段引用。
每一个运行时常量池都分配在 Java 虚拟机的方法区之中,在类和接口被加载到虚拟机后,对应的运行时常量池就被创建出来。
- 运行时常量池中的内容包含:Class常量池中的常量、字符串常量池中的内容
运行时常量池、Class常量池、字符串常量池的区别与联系
虚拟机启动过程中,会将各个Class文件中的常量池载入到运行时常量池中。
所以, Class常量池只是一个媒介场所。在JVM真的运行时,需要把常量池中的常量加载到内存中,进入到运行时常量池。
字符串常量池可以理解为运行时常量池分出来的部分。加载时,对于class的静态常量池,如果字符串会被装到字符串常量池中。
intern
除了双引号形式(字面量)创建字符串对象时,以在运行期将字符串内容放置到字符串常量池的办法外,intern也可以
在每次赋值的时候使用 String 的 intern 方法,如果常量池中有相同值,就会重复使用该对象,返回对象引用。
String 长度限制:
字符串有长度限制,在编译期,要求字符串常量池中的常量不能超过65535,并且在javac执行过程中控制了最大值为65534。
在运行期,长度不能超过Int的范围,否则会抛异常。
java 关键字:
transient
如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。这里的对象存储是指,Java的serialization提供的一种持久化对象实例的机制。当一个对象被序列化的时候,transient型变量的值不包括在序列化的表示中,然而非transient型的变量是被包括进去的。使用情况是:当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。
简单点说,就是被transient修饰的成员变量,在序列化的时候其值会被忽略,在被反序列化后, transient 变量的值被设为初始值, 如 int 型的是 0,对象型的是 null。
ArrayList
类和Vector
类都是使用数组实现的,但是在定义数组elementData
这个属性时稍有不同,那就是ArrayList
使用transient
关键字
instanceof
instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
volatile
Java语言为了解决并发编程中存在的原子性、可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized
、volatile
、final
、concurren包
等
对于volatile
变量,当对volatile
变量进行写操作的时候,JVM会向处理器发送一条lock前缀的指令,将这个缓存中的变量回写到系统主存中。
但是就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题,所以在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议
缓存一致性协议:每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作的时候,会强制重新从系统内存里把数据读到处理器缓存里。
所以,如果一个变量被volatile
所修饰的话,在每次数据变化之后,其值都会被强制刷入主存。而其他处理器的缓存由于遵守了缓存一致性协议,也会把这个变量的值从主存加载到自己的缓存中。这就保证了一个volatile
在并发编程中,其值在多个缓存中是可见的。
保证:可见性、防止指令重排保证有序性
不可保证原子性,volatile可以保证inc
在多个线程之间的可见性。但是无法inc++
的原子性
final
final是Java中的一个关键字,它所表示的是“这部分是无法修改的”。
使用 final 可以定义 :变量、方法、类。
static
我们用static表示变量的级别,一个类中的静态变量,不属于类的对象或者实例。因为静态变量与所有的对象实例共享,因此他们不具线程安全性。
通常,静态变量常用final关键来修饰,表示通用资源或可以被所有的对象所使用。如果静态变量未被私有化,可以用“类名.变量名”的方式来使用。
-
static静态方法
与静态变量一样,静态方法是属于类而不是实例。
一个静态方法只能使用静态变量和调用静态方法。通常静态方法通常用于想给其他的类使用而不需要创建实例。例如:Collections class(类集合)。
Java的静态块是一组指令在类装载的时候在内存中由Java ClassLoader执行。
静态块常用于初始化类的静态变量。大多时候还用于在类装载时候创建静态资源。
Java不允许在静态块中使用非静态变量。一个类中可以有多个静态块,尽管这似乎没有什么用。静态块只在类装载入内存时,执行一次。
Java可以嵌套使用静态类,但是静态类不能用于嵌套的顶层。
静态嵌套类的使用与其他顶层类一样,嵌套只是为了便于项目打包。
const
const是Java预留关键字,用于后期扩展用,用法跟final相似,不常用
http://47.103.216.138/archives/2509
http://47.103.216.138/archives/2550