1.1 java语言有哪些优点?
1.java语言为纯面向对象的语言。
2.平台无关性。java语言的优点便是“一次编译,到处执行”。编译后的程序不会被平台所约束,因此java语言有很好的移植性。
3.java提供了许多内置的类库,将代码封装好来给操作人员使用,从而大大减少开发人员的时间。
4.提供了对web应用的开发的支持。
5.具有较好的安全性和健壮性。
6.去除了c++语言中难以理解,容易混淆的特性,例如头文件,指针,结构,单元运算符重载,虚拟基础类,多重继承等,所以java语言是由c++语言改进并重新设计而来的
1.2 java语言和c/c++有什么异同。
java和C++都是面向对象的语言,都使用了面向对象的思想(例如封装,继承,多态),由于面向对象有许多非常好的特性(继承,组合等),因此二者都有很好的重用性。
下面重点说一下不同点:
1.java为解释型语言,c/c++为编译型语言,java代码由java编译器编译成字节码,然后由JVM解释,C语言代码经过编译和链接生成可执行的二进制代码,因此java的执行速度比c++慢,但是java可跨平台执行,c/c++不能
2.java语言没有指针
3.java只能实现单重继承,但是可以引入多个接口
4.java为纯面向对象语言,所有代码必须在类里实现
5.java语言提供了垃圾回收器来实现对垃圾的自动回收,c++语言中需要开发人员去管理对内存的分配。C语言,通常会把释放资源的代码放在析构函数中,Java没有但是有finalize()方法。
java语言不支持运算符重载,C语言支持
java不支持默认函数参数,c语言支持
java不提供goto语句,c/c++支持,但是在java中goto为保留关键字
java不支持自动强制类型装换,c语言支持
java具有平台无关性,就是对每种数据类型分配固定长度。
java提供对注释文档的内建支持
java包含了一些标准库
1.3 为什么使用public static void main(String[] args)方法?
main是程序的入口方法,所以程序执行时第一个执行的方法就是main方法。
main()方法定义的其他几种格式:
1.static pubic void main(String[] args)
static 和public无先后顺序
2.public static final void main(String[] args)
可以定义为final
3.static public synchronized void main(String[] args)
可以定义为synchronized
不管哪种定义方式,必须保证main()方法类型为void并且有static和public关键字修饰。不可以用abstract关键字,因为main()为程序的入口方法。
1.4静态块
静态块会在类被加载时调用,可以在main()方法前执行
例如:
public class jingtaikuai {
1 | public static void main(String[] args) { |
}
执行结果:
静态块
hello word
1.5 java程序初始化顺序是怎样的
java程序的初始化一般遵循三个原则(优先级依次递减):
1.静态对象优先于非静态对象
2.父类优先于子类
3.按照成员变量定义顺序进行初始化
常见面试题:
下面代码的运行结果是什么?
1 | class B extends Object{ |
执行结果:
load b1
load b2
load a
create b
create a
1.6 java作用域
在Java语言中,变量的类型主要有3种:成员变量、静态变量和局部变量
首先说静态变量跟局部变量
静态变量不依赖于特定的实例,而是被所有实例共享,也就是说,只要一个类被加载,JVM就会给类的静态变量分配
存储空间。因此可以通过类名.变量名来访问静态变量
局部变量的作用域与可见性为它所在的花括号内
类的成员变量的作用范围同类的实例化对象的作用范围相同。当类被实例化的时候,成员变量就会在内存中分配空间,并初始化。
直到类的实例化对象的生命周期结束时,成员变量的生命周期才结束。
作用域与可见性 | 当前类 | 同一package | 子类 | 其他package |
---|---|---|---|---|
public | √ | √ | √ | √ |
private | √ | × | × | × |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
重点说一下protected和default:
protected:表名成员变量或方法对该类自身,与它在同一个包中的其他类,在其他包中的该类的子类都可见
default:表明该成员变量或方法只有自己和与其位于同一包内的类可见。
若父类与子类处于同一包内,则子类对父类的default成员变量或方法都有访问权限;若父类与子类处于不同的package内,则没有访问权限
还有需要注意的是,这些修饰符只能修饰成员变量,不能修饰局部变量。
private和protected不能用来修饰类
1.7 一个java文件中能否定义多个类
一个java文件中可以定义多个类,但是最多只能有一个类被public修饰,并且这个类的类名必须和文件名相同。
1.8 java的构造函数
一、什么是构造函数
java构造函数,也叫构造方法,是java中一种特殊的函数。函数名与相同,无返回值。
作用:一般用来初始化成员属性和成员方法的,即new对象产生后,就调用了对象了属性和方法。
在现实生活中,很多事物一出现,就天生具有某些属性和行为。比如人一出生,就有年龄、身高、体重、就会哭;汽车一出产,就有颜色、有外观、可以运行等。这些,我们就可以将这些天然的属性和行为定义在构造函数中,当new实例化对象时,也就具有这些属性和方法了,没必要再去重新定义了,从而加快了编程效率。
构造函数是对象一建立就运行,给对象初始化,就包括属性,执行方法中的语句。
而一般函数是对象调用才执行,用”.方法名“的方式,给对象添加功能。
一个对象建立,构造函数只运行一次。
而一般函数可以被该对象调用多次。
二、构造函数的特点
1、函数名与类名相同
2、不用定义返回值类型。(不同于void类型返回值,void是没有具体返回值类型;构造函数是连类型都没有)
3、不可以写return语句。(返回值类型都没有,也就不需要return语句了)
注:一般函数不能调用构造函数,只有构造函数才能调用构造函数。
三、示例
1、无参构造函数中只定义了一个方法。new对象时,就调用与之对应的构造函数,执行这个方法。不必写“.方法名”。
1 | package javastudy; |
输出:Hellow World
2、有参构造函数,在new对象时,将实参值传给private变量,相当于完成setter功能。
1 | package javastudy; |
输出:
zhangsan
3
以上代码,我们也可以将show()方法中的输出语句直接放在构造函数中,new对象时,即可直接输出值,如下
1 | package javastudy; |
输出:
zhangsan
3
或
1 | class ConFun |
3、一个对象建立后,构造函数只运行一次。
如果想给对象的值再赋新的值,就要使用set和get方法,此时是当做一般函数使用
如下:
1 | package javastudy; |
输出结果:
姓名:李三年龄:33
阿尔法狗
四、默认构造函数
当一个类中没有定义构造函数时,系统会给该类中加一个默认的空参数的构造函数,方便该类初始化。只是该空构造函数是隐藏不见的。
如下,Person(){}这个默认构造函数是隐藏不显示的。
1 | class Person |
当在该类中自定义了构造函数,默认构造函数就没有了。
如果仍要构造函数,则需要自己在类中手动添加。
五、构造函数的重载
构造函数也是函数的一种,同样具备函数的重载(Overloding)特性。
1 | class Person |
输出结果:
A:name=null:::age=0
B:name=lishi:::age=0
C:name=lishi:::age=10
1 | class Person |
输出结果:
A:name=null:::age=0
Cry……………
B:name=lishi:::age=0
Cry……………
C:name=lishi:::age=10
Cry……………
转自:http://www.cnblogs.com/ibelieve618/p/6364541.html
1.9 java中的clone方法
java中所有的类都继承自Object类,这个类提供了一个clone的方法,这个方法的作用是返回一个Object对象的复制。
使用步骤:
1.继承Cloneable 接口
2.重写clone()方法
3.clone方法中调用super.clone()
4.把浅复制的引用指向原型对象新的克隆体
一、简单用法
只需要在需要clone的对象上实现(implements)Cloneable接口,然后再在类中加上clone方法,在方法中只需要调用super.clone(),根据自己的需要实现即可。
1 | public class Student implements Cloneable { |
}
输出结果:
testClone.Student@15db9742 age: 1 name: aa
testClone.Student@6d06d69c sC.age: 1 sC.name: aa
testClone.Student@15db9742 age: 1 name: aa
testClone.Student@6d06d69c sC.age: 12 sC.name: bb
分析结果:1、根据输出结果中前边的类名,可以得出被克隆对象的与原来的对象是同一种类型。2、根据内存地址(hashcode)知道,被克隆对象的与原来的对象是存在于内存中的不同的两个对象。所以后边有一个赋值,对原来对象没有任何影响。
二、“影子”克隆与深度克隆
首先看一个例子:
1 | class Bag{//学生的书包 |
}
输出结果:
testClone.Student2@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike width: 10)
testClone.Student2@7852e922 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike width: 10)
testClone.Student2@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(JNike width: 100)
testClone.Student2@7852e922 age: 12 name: bb bag: testClone.Bag@6d06d69c(JNike width: 100)
分析:发现是不是跟预期的不太一样,通过第二个同学改变书包,但是第一个同学的书包也被改变了。并且通过内存地址可知,他们是同一对象(书包)。原因:调用Object类中clone()方法产生的效果是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内 容。对基本数据类型,这样的操作是没有问题的,但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,这也导致clone后的非基本类型变量和原始对 象中相应的变量指向的是同一个对象。 这就是所谓的“影子”克隆。
解决方案:深度克隆,既是对里边的引用也要克隆。以下是实现:
1 | class Bag implements Cloneable{//学生的书包 |
}
输出:
testClone.Student3@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike width: 10)
testClone.Student3@7852e922 age: 1 name: aa bag: testClone.Bag@4e25154f(Nike width: 10)
testClone.Student3@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike width: 10)
testClone.Student3@7852e922 age: 12 name: bb bag: testClone.Bag@4e25154f(JNike width: 100)
1.10 什么是反射机制
Java 反射机制是在运行状态中,对于任意一个类,都能够获得这个类的所有属性和方法,对于任意一个对象都能够调用它的任意一个属性和方法。这种在运行时动态的获取信息以及动态调用对象的方法的功能称为Java 的反射机制。
Class 类与java.lang.reflect 类库一起对反射的概念进行了支持,该类库包含了Field,Method,Constructor类(每个类都实现了Member 接口)。这些类型的对象时由JVM 在运行时创建的,用以表示未知类里对应的成员。
这样你就可以使用Constructor 创建新的对象,用get() 和set() 方法读取和修改与Field 对象关联的字段,用invoke() 方法调用与Method 对象关联的方法。另外,还可以调用getFields() getMethods() 和 getConstructors() 等很便利的方法,以返回表示字段,方法,以及构造器的对象的数组。这样匿名对象的信息就能在运行时被完全确定下来,而在编译时不需要知道任何事情。
二、获取字节码的方式
在Java 中可以通过三种方法获取类的字节码(Class)对象
通过Object 类中的getClass() 方法,想要用这种方法必须要明确具体的类并且创建该类的对象。
所有数据类型都具备一个静态的属性.class 来获取对应的Class 对象。但是还是要明确到类,然后才能调用类中的静态成员。
只要通过给定类的字符串名称就可以获取该类的字节码对象,这样做扩展性更强。通过Class.forName() 方法完成,必须要指定类的全限定名,由于前两种方法都是在知道该类的情况下获取该类的字节码对象,因此不会有异常,但是Class.forName() 方法如果写错类的路径会报 ClassNotFoundException 的异常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23ackage com.jas.reflect;
public class ReflectTest {
public static void main(String[] args) {
Fruit fruit = new Fruit();
Class<?> class1 = fruit.getClass(); //方法一
Class<?> class2 = Fruit.class; //方法二
Class class3 = null;
try { //方法三,如果这里不指定类所在的包名会报 ClassNotFoundException 异常
class3 = Class.forName("com.jas.reflect.Fruit");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(class1 + " " +class2 + " " + class3);
}
}
class Fruit{}
三、通过反射机制获取类信息
通过反射机制创建对象,在创建对象之前要获得对象的构造函数对象,通过构造函数对象创建对应类的实例。
下面这段代码分别在运行期间创建了一个无参与有参的对象实例。由于getConstructor() 方法与newInstance() 方法抛出了很多异常(你可以通过源代码查看它们),这里就简写了直接抛出一个Exception,下同。
1 | package com.jas.reflect; |
输出:
无参构造器Run………..
有参构造器Run………..Apple
通过反射机制获取Class 中的属性
1 | package com.jas.reflect; |
输出:
无参构造器Run………..
Apple
通过反射机制获取Class 中的方法并运行。
1 | package com.jas.reflect; |
输出:
Fruit type = Apple
Fruit type = Apple…..Fruit num = 20
四、反射机制简单应用(使用简单工厂创建对象)
Class.forName() 生成的结果是在编译时不可知的,因此所有的方法特征签名信息都是在执行时被提取出来的。反射机制能过创建一个在编译期完全未知的对象,并调用该对象的方法。
以下是反射机制与泛型的一个应用,通过一个工厂类创建不同类型的实例。
要创建对象的实例类Apple :
1 | package com.jas.reflect; |
加载的配置文件config.properties:
1 | Fruit=com.jas.reflect.Apple |
工厂类BasicFactory :
1 | package com.jas.reflect; |
创建对象实例:
1 | package com.jas.reflect; |
输出
com.jas.reflect.Apple@4554617c
上面这个实例通过一个工厂创建不同对象的实例,通过这种方式可以降低代码的耦合度,代码得到了很大程度的扩展,以前要创建Apple 对象需要通过new 关键字创建Apple 对象,如果我们也要创建Orange 对象呢?是不是也要通过new 关键字创建实例并向上转型为Fruit ,这样做是麻烦的。
现在我们直接有一个工厂,你只要在配置文件中配置你要创建对象的信息,你就可以创建任何类型你想要的对象,是不是简单很多了呢?可见反射机制的价值是很惊人的。
Spring 中的 IOC 的底层实现原理就是反射机制,Spring 的容器会帮我们创建实例,该容器中使用的方法就是反射,通过解析xml文件,获取到id属性和class属性里面的内容,利用反射原理创建配置文件里类的实例对象,存入到Spring的bean容器中。
参考书籍:
《Java 编程思想》 Bruce Eckel 著 陈昊鹏 译
1.10 java创建对象的几种方式
- 通过new实例化一个对象
- 通过反射机制创建对象
- 通过clone方法创建一个对象
- 通过反序列化方式创建一个对象
1.11 package作用
package的作用
- package的中文意思是“包”,它是一个比较抽象的逻辑概念,其宗旨是把.java文件 (Java源文件)、.class文件(编译后的文件)以及其他resource文件(例如.xml文件、.avi文件、.mp3文件、.txt文件等)有条理地进行一个组织,以供使用。它类似于Linux文件系统, 有一个根,从根开始有目录和文件,然后目录中嵌套目录。
- 具体而言,package主要有以下两个作用:
第一,提供多层命名空间,解决命名冲突,通过使用package,使得处于不同package中的类可以存在相同的名字。
第二,对类按功能进行分类,使项目的组织更加清晰。当开发一个有非常多的类的项目时,如果不使用package对类进行分类,而是把所有类都放在一个package下,这样的代码不仅可读性差,而且可维护性也不好,会严重影响开发效率。
2)package的用法
- package的用法一般如下(源文件所在目录为当前目录):
1.在每个源文件的开头加上”package packagename;”,然后源文件所在目录下创建一个新目录,名称为 packapename。
2.用javac指令编译每个sourcename. java源文件,将生成的sourcename. classname文件复制到packagename 目录。
3.用 java 指令运行程序:java packagename. sourcename。
3)实例
以下是一个简单的程序示例。
1 | package top.itcourse.pkg; |
结果:
Hello World!