java基础回顾
1.什么是字节码?采用字节码的最大好处是什么
字节码:Java源代码经过虚拟机编译器编译后产生的文件(即扩展为.class文件),他不面向特定的处理器,只面向虚拟机。
采用字节码的最大好处:
java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,Java程序无需重新编译便可在多种不同的计算机上运行。
java中的编译器和解释器:
#
Java中引入了虚拟机的概念,即在机器上和编译程序之间加入了一层抽象和虚拟机器。这台虚拟的机器在任何平台上都提供给编译程序一个共同的接口。编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由解释器将虚拟机代码转换为特定系统的机器码执行。在Java中,这种供虚拟机理解的代码叫做字节码(即扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机。每一种平台的解释器是不同的,但是实现的虚拟机只相同的。Java程序通过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行,这就是上面提到的Java的特点的编译与解释并存的解释。(编译器:javac.exe)
2.java中的float和double
#
double类型代表双精度浮点数,float类型代表单精度浮点数。一个double类型的数值占8个字节,64位、一个float类型的数值占4个字节、32位。
只有浮点类型的数值才可以使用科学计数法的方式,例如 51200 是一个int类型;但是512E2则是浮点类型的值。
java语言的浮点类型默认是double类型。如果希望Java把一个浮点类型值当成float类型处理,因该在这个浮点类型值后紧跟着f或F。例如5.12是一个double类型的值,占64位的内存空间;5.12f或者5.12F才表示一个float类型的值,占32位的内存空间。
#
java 还提供三个特殊的浮点数值:正无穷大、负无穷大和非数,用于表示溢出和出错。
例如:使用一个正数除以0得到正无穷大
使用一个负数除以0得到一个负无穷大
0.0除以0.0或对一个负数开方得到一个非数。
所有的正无穷大的数都是相等的,所有的负无穷大都是相等的;而NaN不与任何数值相等,甚至和NaN都不相等。
3.final 有什么用?
final修饰的变量不可以被改变,意思是:被final修饰的变量不可变的是变量的引用,而不是引用指向的内容:
举例说明“
" class="reference-link">
4.final、finally、finalize的区别
final用于修饰方法、修饰类、修饰变量
finally用于try catch finally中
finalize是一个方法,属于Object类的方法,而Object类是所有类的父类,该方法一般由垃圾回收器调用,当我们掉用System.gc()的时候,由垃圾回收器掉用finalize(),回收垃圾。
#
5.this关键字
this代表对象本身,可以理解位对象本身的一个指针
this的用法在java中可以有三种用法:
a.形参与成员的名字重名
b.引用本类的构造函数
c.普通的直接引用,(同类的方法相互调用时this可以省略)
代码举例
public class ThisDemo {
int A;
int B;
int E;
public ThisDemo(int c, int d, int E) {
A = c;
B = d;
this.E = E;
}
public void cook() {
System.out.println("准备做饭");
this.shop();
this.doing();
}
public void shop() {
System.out.println("先买菜");
}
public void doing() {
System.out.println("开始动手炒菜");
}
public static void main(String[] args) {
ThisDemo thisDemo = new ThisDemo(1, 2, 3);
thisDemo.cook();
}
}
- super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。
- this(参数):调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)。
6.在Java中如何跳出当前嵌套的多重循环,
在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break 语句,即可跳出外层循环。例如:
public static void main(String[] args) {
ok:
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
System.out.println("i=" + i + ",j=" + j);
if (j == 5) {
break ok;
}
}
}
}
7.面向对象和面向过程的区别?
面向过程是具体化的、流程化的,解决一个问题,解决问题一需要一步步的分析,一步步的实现
面向对象是模型化的,你只需要抽出一个类,这是一个封装的盒子,你需要什么功能直接掉用就可以了,
面向对象的底层其实还是面向过程,把面向过程抽象成类,然后封装,方便我们使用的就是面向对象了。
#
8.抽象类和接口的对比
抽象类是用来捕捉子类的通用特性的,接口是抽象方法的集合。
从底层设计来看,抽象类是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。
相同点:
接口和抽象类都不能实例化
都位于继承的顶端,用于被他实现或继承
都包含抽象方法,其子类都必须复写这些抽象方法
8.成员变量和局部变量
成员变量:有默认的初始值
局部变量:没有默认的初始值,使用前必须赋值。
在使用变量时需要遵循的原则为:就近原则
首先在局部范围找,有就使用;接着在成员位置找。
9.内部类
内部类可以分为四种:成员内部类、局部内部类、匿名内部类和静态内部类。
静态内部类
public class Outer {
private static int radius = 1;
static class StaticInner {
public void visit() {
System.out.println("visit outer static variable:" + radius);
}
}
}
静态内部类可以访问外部类所有的鹅静态变量,而不可访问外部类的非静态变量;静态内部类的创建方式,
new 外部类.静态内部类()
public class InnerClassDemo {
private static int num = 1;
static class StaticCla {
public void visit() {
System.out.println(num);
}
}
public static void main(String[] args) {
InnerClassDemo.StaticCla abc = new InnerClassDemo.StaticCla();
abc.visit();
}
}
成员内部类
public class Outer {
private static int radius = 1;
private int count =2;
class Inner {
public void visit() {
System.out.println("visit outer static variable:" + radius);
System.out.println("visit outer variable:" + count);
}
}
}
定义在类内部,成员位置上的非静态类,就是成员内部类。
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.visit();
局部内部类
public class Outer {
private int out_a = 1;
private static int STATIC_b = 2;
public void testFunctionClass(){
int inner_c =3;
class Inner {
private void fun(){
System.out.println(out_a);
System.out.println(STATIC_b);
System.out.println(inner_c);
}
}
Inner inner = new Inner();
inner.fun();
}
public static void testStaticFunctionClass(){
int d =3;
class Inner {
private void fun(){
// System.out.println(out_a); 编译错误,定义在静态方法中的局部类不可以访问外部类的实例变量
System.out.println(STATIC_b);
System.out.println(d);
}
}
Inner inner = new Inner();
inner.fun();
}
}
匿名内部类
匿名内部类就是没有名字的内部类,日常开发中使用的比较多
public class NonNameInnerClassDemo {
interface InnerClass{
void Sing();
}
private void test(){
new InnerClass(){
@Override
public void Sing() {
System.out.println("就是唱呀!");
}
}.Sing();
}
@Test
public void Test1(){
test();
}
}
除了没后名字,匿名内部类还有以下特点:
匿名内部类必须继承一个抽象类或者实现一个接口。
匿名内部类不能定义任何静态成员的静态方法。
当所在的方法的形参需要被匿名内部类使用时,必须声明为final。
匿名内部类不能是抽象的,他必须要实现继承的类或者实现的接口的所有的抽象方法。
内部类的优点:
- 一个内部类对象可以访问创建它的外部类对象的内容、方法包括是有数据
- 内部类不为同一包的其它类所见,具有很好的封装性;
- 内部类有效实现了“多重继承”,优化Java单继承的缺陷。
- 匿名内部类可以很方便的定义回调
匿名/局部内部类访问局部变量时,为什么局部变量必须加final
我们都知道方法中的匿名/局部内部类是能够访问同一个方法中的局部变量的,但是为什么局部变量要加上一个final呢?
首先我们来研究一下变量生命周期的问题,局部变量的生命周期是当该方法被调用时在栈中被创建,当方法调用结束时(执行完毕),退栈,这些局部变量就会死亡。但是内部类对象是创建在堆中的,其生命周期跟其它类一样,只有当jvm用可达性分析法发现这个对象通过GCRoots节点已经不可达,然后进行gc才会死亡。
所以完全有可能存在的一个现象就是一个方法已经调用结束(局部变量已死亡),但该内部类的对象仍然活着,也就是说内部类创建的对象的生命期会超过局部变量,这就会出现内部类的对象要访问局部变量时访问失败,而java为了保证这种情况不会发生,就用了一种折中的方案:内部类要访问局部变量,局部变量必须加final,那为什么局部变量定义成final就可以了呢?
然后我们再来看final这个关键字修饰变量的特点:当final修饰一个基本数据类型的变量时,这个基本类型的变量在初始化的时候就应该赋值,并且初始化完成之后其值就不能再发生改变了;当final修饰一个引用类型的变量时,这个引用类型的变量在初始化的时候就应该赋值并且之后不能再发生改变,但是引用类型的变量指向的对象的堆内存的值是可以改变的。
基于final修饰变量的以上两个特点,java就把局部内部类对象要访问的final型局部变量,复制过来变成该内部类对象中的一个成员变量,这样即使栈中局部变量(含final)已死亡,但由于它是final的,其值是不会发生改变的,因而内部类对象在局部变量死亡后,照样可以访问自己内部维护的一个值跟局部变量一样的成员变量,从而解决这个问题。
举个例子来说可能会更清楚一些,对于局部变量int i=3;方法中代码修改的是这个真正的变量i,而内部为对象修改的是i的复制品copy_i,这样这两个i就会发生值的不一致性,(这一点正是这个实现技术的缺陷),所以干脆就不允许这个int i=3;局部变量发生值的改变!由于不允许改int i的值,所以这两个int i的值就始终保持值的一致了,这才是final的这个规定的由来! 是一种不得不如此的无奈之举!
简单的来说,因为生命周期的原因,内部类需要复制局部变量为内部类的一个成员变量,因为复制,为了保证局部变量和该副本的值的一致性,所以要将修饰符设为final。
10 IO——BIO,NIO,AIO 有什么区别?
BIO(Blocking I/O):同步阻塞IO模型
NIO(New I/O):同步非阻塞IO模型
AIO(Asynchronous I/O):异步非阻塞IO模型
11 反射
什么是反射机制?
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
反射机制的优缺点
优点:运行期类型的判断,动态加载类,提高代码灵活度。
缺点:性能瓶颈:反射相当于一系列解释操作,通知JVM要做的事情,性能比直接的Java代码要慢很多。
反射机制的应用场景有哪些?
②Spring框架也用到很多反射机制,最经典的就是xml的配置模式。Spring 通过 XML 配置模式装载 Bean 的过程:1) 将程序内所有 XML 或 Properties 配置文件加载入内存中; 2)Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息; 3)使用反射机制,根据这个字符串获得某个类的Class实例; 4)动态配置实例的属性
举例:
<!-- 数据源配置, 使用DBCP数据库连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName">
<value>${jdbc.driverClassName}</value>
</property>
<property name="url">
<value>${jdbc.url}</value>
</property>
<property name="username">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
<property name="initialSize">
<value>${jdbc.initialSize}</value>
</property>
<property name="maxActive">
<value>${jdbc.maxActive}</value>
</property>
<property name="maxIdle">
<value>${jdbc.maxIdle}</value>
</property>
<property name="minIdle">
<value>${jdbc.minIdle}</value>
</property>
<property name="timeBetweenEvictionRunsMillis">
<value>${jdbc.timeBetweenEvictionRunsMillis}</value>
</property>
<property name="testWhileIdle">
<value>${jdbc.testWhileIdle}</value>
</property>
<property name="validationQuery">
<value>${jdbc.validationQuery}</value>
</property>
</bean>
<!-- Spring 集成 mybatis -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="dataSource" ref="dataSource"/>
</bean>
Java获取反射的三种方法
- 通过new对象实现反射机制
- 通过路径实现反射机制
- 通过类名实现反射机制
12.String
什么是字符串常量池?
字符串常量池位于堆内存中,专门用来存储字符串常量,可以提高内存的使用率,避免开辟多块空间存储相同的字符串,在创建字符串时JVM会首先检查字符串常量池,如果该字符已经存在池中,则返回它的引用,如果不存在,则实例化一个字符串放在池中,并返回其引用。
String是最基本的数据类型吗?
不是,Java中最基本的数据类型有8个:byte,short,int,long,bollean,float,double,char;
String有哪些特性
不变性:String是只读字符串,是一个典型的immutable对象,对他进行任何操作,其实都是创建一个新的对象,再把引用指向该对象。不变模式的主要作用在于当一个对象需要被多线程共享并频繁访问时,可以保证数据的一致性。
常量池优化:String对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。
final:使用final来定义String类,表示String类不能被继承,提高了系统的安全性。
String真的时不可变的吗?
1)String不可变但不代表引用不可以变
String str = "Hello";
str = str + " World";
System.out.println("str=" + str);
结果
str=Hello World
解析:实际上,原来String的内容是不可变的,只是String由原来指向“Hello”的内存地址转为指向“Hello World”的内存地址而已,也就是多开辟了一块内存区域给“Hello World”字符串。
2)通过反射是可以修改所谓的“不可变”对象
// 创建字符串"Hello World", 并赋给引用s
String s = "Hello World";
System.out.println("s = " + s); // Hello World
// 获取String类中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");
// 改变value属性的访问权限
valueFieldOfString.setAccessible(true);
// 获取s对象上的value属性的值
char[] value = (char[]) valueFieldOfString.get(s);
// 改变value所引用的数组中的第5个字符
value[5] = '_';
System.out.println("s = " + s); // Hello_World
结果:
s = Hello World
s = Hello_World
String s = new String(“xyz”);创建了几个字符串对象
两个对象,一个是静态区的“xyz”,一个是用new创建在堆上的对象。
String str1 = "hello"; //str1指向静态区
String str2 = new String("hello"); //str2指向堆上的对象
String str3 = "hello";
String str4 = new String("hello");
System.out.println(str1.equals(str2)); //true
System.out.println(str2.equals(str4)); //true
System.out.println(str1 == str3); //true
System.out.println(str1 == str2); //false
System.out.println(str2 == str4); //false
System.out.println(str2 == "hello"); //false
str2 = str1;
System.out.println(str2 == "hello"); //true
在使用HashMap的时候,用String做key有什么好处?
HashMap内部实现是通过key的hashcode来确定value的存储位置,因为字符串是不可变的,所以当创建字符串时,它的hashcode被缓存下来,不需要再次计算,所以相比于其他对象更快。
String和StringBuffer、String Builder的区别是什么?String为什么是不可变的?
可变性
String类中使用字符数组保存字符串,private final char value[],所以string 对象是不可变的。
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[] value, 这两种对象是可变的。
线程安全性
String中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder是StringBuilderStringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁或者对调用者
性能
每次对String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象。StringBuffer每次都会对StringBuffer对象本身进行宝座,而不是生成新的对象并改变对象引用。相同情况下使用StringBuilder相比使用StringBuffer仅能获得10%~15%左右的性能提升,但却要冒多线程不安全的风险。
对于三个使用的总结
如果操作少量的数据 = String
单线程操作字符串缓冲区下操作大量数据 = StringBuilder
多线程操作字符串缓冲区下操作大量数据=StringBuffer
13 包装类相关
Integer a=127 与Integer b=127 相等吗?
对于对象引用类型:== 比较的是对象的内存地址。
对于基本数据类型:==比较的是值。
如果整型字面量的值在-128到127之间,那么自动装箱是不会new新的Integer对象,而是直接引用常量池中的Integer对象,超过范围a1==b1的结果是false
public static void main(String[] args) {
Integer a = new Integer(3);
Integer b = 3; // 将3自动装箱成Integer类型
int c = 3;
System.out.println(a == b); // false 两个引用没有引用同一对象
System.out.println(a == c); // true a自动拆箱成int类型再和c比较
System.out.println(b == c); // true
Integer a1 = 128;
Integer b1 = 128;
System.out.println(a1 == b1); // false
Integer a2 = 127;
Integer b2 = 127;
System.out.println(a2 == b2); // true
}
还没有评论,来说两句吧...