java程序设计基本概念_1、java程序设计基本概念

这些都是比较基础的内容,但是容易被问到。

1、JVM

参考:

http://www.jianshu.com/p/1c75446d527f

2、i++

package com.baodian;

public class Test {

public static void main(String[] args) {

int j = 0;

for(int i = 0; i < 100; i++){

j = j++;

}

System.out.println(j);

}

}

输出为0,可以这样理解,运算j++的值为0,然后j = 1,之后将0赋值给值为1的j。

3、运算符

int a = 5;

System.out.println(a < 5 ? 10.9 : 9);

由于前面的10.9,后面的9会进行类型转换而变成9.0.

char x = 'x';

int i = 10;

System.out.println(false ? i : x);

System.out.println(false ? 10 : x);

因为i是一个变量,所以第一个输出的字符x被转换成了int类型,于是输出120,而第二个输出中有一个是常量,另一个是T(char)类型,当常量可以被T表示时,输出结果是T类型,于是输出x。

4、异常

1107f2af60cb

1

要记住的几点是:

运行时异常(RuntimeException)是那些可能在Java虚拟机正常运行期间抛出的异常的超类,不需要捕获。这种异常的特点是Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try...catch语句捕获它,也没有用throws字句声明抛出它,还是会编译通过。

错误是不能被捕获的

try、catch、finally三个代码块中变量的作用域分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面

抛出异常可以这样:方法声明的时候用了throws语句,方法中throw语句,方法调用的方法声明有throws关键字

throw和throws关键字的区别:throw用来抛出一个异常,在方法体内。throws用来声明方法可能会抛出什么异常,在方法名后

对于IOException,这是受检查异常,要么捕获,要么抛出。

两种异常的区别:运行时异常表示无法让程序恢复运行的异常,导致这种异常的原因通常是由于执行了错误的操作。一旦出现错误,建议让程序终止。受检查异常表示程序可以处理的异常。如果抛出异常的方法本身不处理或者不能处理它,那么方法的调用者就必须去处理该异常,否则调用会出错,连编译也无法通过。当然,这两种异常都是可以通过程序来捕获并处理的。

finally中的代码是在return之前执行。

5 final、finally、finalize的区别

5.1 final

(1)final 成员

当在类中定义变量时,若在其前面加上final关键字,也就表示这个变量一旦被初始化,便不可改变,这里的不可改变是说对基本类型其值不可改变,而对于对象变量来说其引用不可变。其初始化可以在两个地方,一是其定义处,二是在构造函数中,两者只能选其一。

(2)final 方法

将方法声明为final有两个原因,第一是次方法不需要扩展了,但是可以继承,即可以直接使用。第二是对次方法的调用转换为inline(行内)调用的机制,即在调用final方法时,直接将方法主体插入到调用处,而不是进行例行的方法调用,这样可以提高效率,但是会使得方法主体变得很大。

(3)final 类

将final用于类的时候表示此类不需要扩展了,其成员可以是final也可以不是,而其中的方法自然就是final。

5.2 finally

用在异常代码块中,其中的代码总会执行,而且是在return之前执行。

5.3 finalize

次finalize用于一些不容易控制,并且非常重要的资源的释放,如一些io的操作、数据的连接。java中允许finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。此方法是在垃圾收集器删除对象之前对这个对象调用的。

6、 传递与引用

6.1 传值与引用

我们要记住一点:不管java参数的类型是什么,一律传递参数的副本。

在java中,变量分为两类:

(1)对于基本类型,传递的是其值的副本。

(2)对于一切对象变量,java传递的都是引用的副本,也就是复制指向地址的指针。

注意:String类型也是对象,所以必然传递的是引用副本。只不过String是一个非可变类,使得其传值还是传引用显得没有区别而已。下面看两个例子:

public class Test{

public static void test(StringBuffer str){

str.append(", World!");

}

public static void main(String[] args){

StringBuffer string = new StringBuffer("Hello");

test(string);

System.out.println(string);

}

}

说明:这里传递的是引用,而方法中将引用指向的对象的值改变了,于是最后输出结果为Hello, World!

public class Test{

public static void test(String str){

str = "World";

}

public static void main(String[] args) {

String string = "Hello";

test(string);

System.out.println(string);

}

}

说明:这里同样我们也是传递的引用,但是当执行str="World;"时其过程为,首先系统生成一个新String对象,并把这个对象的值设置为World,然后把这个对象的引用付给str,于是str的引用变了,但是我们要知道String是final类型,引用副本的改变和原来的地址没有任何关系,原来地址指向的对象的值是没有改变的。

6.2 序列化

对象序列化能将一个实现了Serializable接口的对象转换成一组byte,这样日后要用这个对象的时候就能使用这组byte重新构建这个对象。这点甚至跨网络环境下也是如此。其实对象序列化就是一种轻量级的持久化。

对象序列化有两个重要的功能。(1)java的远程方法调用能像调用自己机器上的对象哪有那个去调用其他机器上的对象;(2)对于javabean这是必不可少的。bean的状态信息通常是在设计时配置的,这些状态信息必须保存起来,供程序启动的时候用,对象序列化就负责次工作。

7、循环与条件

例1:

import java.util.*;

public class Test{

public static void main(String[] args) {

for(int i = 0; i <= 10; i++)

Integer k = new Integer(i);

System.out.println("Hello World");

}

}

说明:这个程序在编译的时候会出现错误,局部变量声明的作用范围是在一个块内,可以理解为在"{}"内。for循环可以不使用"{}",但仅限于执行语句(其中并不包括变量声明语句),由于这段代码中Integer k的作用范围在整个main中,这样就造成了变量的重复定义的错误。我们可以给for循环加上一个花括号。

例2:

有一个整数n,写一个函数f(n),返回0~n之间出现的1的次数。比如f(1)=1;f(13)=6,问一个最大的f(n)=n中的n是多少。

package com.baodian;

public class DemoP77 {

public static void main(String[] args) {

int n = 2;

int res = 1;

while((getOnly(n) + res) != n){

res = getOnly(n);

++n;

}

System.out.println(n);

}

public static int getOnly(int num){

int number = 0;

String str = num + "";

if(str.length() > 0){

for(int i = 0; i < str.length(); i++){

if(str.charAt(i) == '1'){

number++;

}

}

}

return number;

}

}

说明:这个题目的关键在效率上,在没有发现很科学、快速的计算出个数的情况下,可以采用缓存机制。因为就20000来说,计算时间已经无法忍受了,因此可以把前面的计算结果缓存下来,这样就不用每次都重新计算之前计算过的数值。如在计算101时只要把之前1~100的结果和101相加即可。

8、内存管理

8.1 垃圾收集

例1:

下面说法中哪项是正确的?

A、java虚拟机中的自动垃圾回收阻止程序运行内存溢出

B、一段程序可以建议垃圾回收执行,但是不能强迫它执行

C、垃圾回收是一个独立的平台

D、当一个对象的所有引用都被置空时,这个对象就可以变为能被垃圾回收

说明:

此题答案为B、D。如果一个程序一直不断创建一个引用对象时,其他的引用又没有被抛弃,则会造成内存耗尽的结果。垃圾回收并不是一个独立的平台,具有平台依赖性。

垃圾回收机制:gc即垃圾回收机制,是指JVM用于释放那些不再使用的对象所占用的内存。java语言并不要求JVM有gc,也没有规定其如何工作。gc是为所有的java应用进程服务的,而不是为某个特定的进程服务的。因此,任何一个进程都不能要求gc做什么。在JVM垃圾收集器收集一个对象之前,一般要求程序调用适当的方法释放资源,但在没有明确释放资源的情况下,java提供了默认机制终止给对象来释放资源,这个方法就是finalize()。此方法执行后,gc开始执行,对象消失。

请再看以下三种认证考试时可能出现的题型:

程序段1:

1.fobj = new Object ( ) ;

2.fobj. Method ( ) ;

3.fobj = new Object ( ) ;

4.fobj. Method ( ) ;

问:这段代码中,第几行的fobj 符合垃圾收集器的收集标准?

答:第3行。因为第3行的fobj被赋了新值,产生了一个新的对象,即换了一块新的内存空间,也相当于为第1行中的fobj赋了null值。这种类型的题在认证0考试中是最简单的。

程序段2:

1.Object sobj = new Object ( ) ;

2.Object sobj = null ;

3.Object sobj = new Object ( ) ;

4.sobj = new Object ( ) ;

问:这段代码中,第几行的内存空间符合垃圾收集器的收集标准?

答:第1行和第3行。因为第2行为sobj赋值为null,所以在此第1行的sobj符合垃圾收集器的收集标准。而第4行相当于为sobj赋值为null,所以在此第3行的sobj也符合垃圾收集器的收集标准。

如果有一个对象的句柄a,且你把a作为某个构造器的参数,即 new Constructor ( a )的时候,即使你给a赋值为null,a也不符合垃圾收集器的收集标准。直到由上面构造器构造的新对象被赋空值时,a才可以被垃圾收集器收集。

程序段3:

1.Object aobj = new Object ( ) ;

2.Object bobj = new Object ( ) ;

3.Object cobj = new Object ( ) ;

4.aobj = bobj;

5.aobj = cobj;

6.cobj = null;

7.aobj = null;

问:这段代码中,第几行的内存空间符合垃圾收集器的收集标准?

答:第7行。注意这类题型是认证考试中可能遇到的最难题型了。

行1-3分别创建了Object类的三个对象:aobj,bobj,cobj

行4:此时对象aobj的句柄指向bobj,所以该行的执行不能使aobj符合垃圾收集器的收集标准。

行5:此时对象aobj的句柄指向cobj,所以该行的执行不能使aobj符合垃圾收集器的收集标准。

行6:此时仍没有任何一个对象符合垃圾收集器的收集标准。

行7:对象cobj符合了垃圾收集器的收集标准,因为cobj的句柄指向单一的地址空间。在第6行的时候,cobj已经被赋值为null,但由cobj同时还指向了aobj(第5行),所以此时cobj并不符合垃圾收集器的收集标准。而在第7行,aobj所指向的地址空间也被赋予了空值null,这就说明了,由cobj所指向的地址空间已经被完全地赋予了空值。所以此时cobj最终符合了垃圾收集器的收集标准。 但对于aobj和bobj,仍然无法判断其是否符合收集标准。

总之,在Java语言中,判断一块内存空间是否符合垃圾收集器收集标准的标准只有两个:

1.给对象赋予了空值null,以下再没有调用过。

2.给对象赋予了新值,既重新分配了内存空间。

最后再次提醒一下,一块内存空间符合了垃圾收集器的收集标准,并不意味着这块内存空间就一定会被垃圾收集器收集。

8.2 内存管理

例1:java是如何管理内存的?

在java中,内存管理就是对象的分配和是释放问题。程序员需要通过new为每个对象申请内存空间,所有的对象都在堆(heap)中分配空间。另外,对象的释放是由gc决定和执行的。在java中,内存的分配是由程序员完成的,而内存的释放是由gc完成的,这种收支两条线的方法确实简化了程序员的工作。但同时,它也加重了JVM的工作。这也是java程序运行较慢的原因之一。因为gc为了能够正确释放对象,必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,gc都需要进行监控。我们可以将对象考虑为有向图的的顶点,将引用关系考虑为图的有向边,有向边从引用指向被引用的对象。

例2:什么是java中的内存泄漏?

在 java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连(也就是说仍存在该内存对象的引用);其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为 java中的内存泄漏,这些对象不会被 gc所回收,然而它却占用内存。

例3:JVM的内存区域组成

在java中把内存分两种:一种是栈内存,另一种是堆内存。1.在函数中定义的基本类型变量和对象的引用变量都在函数的栈内存中分配;2.堆内存用来存放由 new创建的对象和数组以及对象的实例变量。在函数(代码块)中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量所分配的内存空间;在堆中分配的内存由java虚拟机的自动垃圾回收器来管理

堆和栈的优缺点

堆的优势是可以动态分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的。缺点就是要在运行时动态分配内存,存取速度较慢; 栈的优势是,存取速度比堆要快,仅次于直接位于 CPU中的寄存器。另外,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。

8.3 clone

例1:Object类中有clone方法,但是Object又没有实现cloneable接口,这是为什么?对于一个没有实现cloneable的类来说,还可以从Object类继承而来的clone方法实现一些基本的值的复制操作,那是不是可以说clone方法并没有对对象是否属于cloneable类型进行检测?

答:clone方法是在Object中定义的,而且是protected型的,只有实现了这个接口,才可以在该类的实例上调用clone方法,否则会抛出CloneNotSupportException。说clone方法并没有对对象是否属于cloneable类型进行检测是不对的。因为cloneable接口的出现跟接口的正常使用没有任何关系,特别是它并不指定clone方法-该方法从Object类继承而来的,该接口只是作为一个标记。这句话通俗地说就是一下三点:首先,clone方法是Object类的一个方法,所以任何一个类都会自动拥有这个方法。其次,这并不说明该类就可以调用clone了,因为javac或者java需要程序员显式地指明该类可以调用clone,方法就是写上这个字符串"implements Cloneable"。再次,这个字符串只是起一个指示作用,没有其他功能,这是由javac或者java规定的。

(1)对象的clone

(摘自:http://blog.csdn.net/zhangjg_blog/article/details/18369201)

在Java中,以下类似的代码非常常见:

Person p = new Person(23, "zhang");

Person p1 = p;

System.out.println(p);

System.out.println(p1);

从打印结果可以看到两个对象的地址是一样的。既然地址都是相同的,那么肯定是同一个对象。p和p1只是引用而已。上面代码执行完成之后, 内存中的情景如下图所示:

1107f2af60cb

1

而下面的代码是真真正正的克隆了一个对象:

Person p = new Person(23, "zhang");

Person p1 = (Person) p.clone();

System.out.println(p);

System.out.println(p1);

此时打印的结果是不一样的,以上代码执行完成后, 内存中的情景如下图所示:

1107f2af60cb

2

(2)深拷贝 or 浅拷贝

上面的示例代码中,Person中有两个成员变量,分别是name和age, name是String类型, age是int类型。代码非常简单,如下所示:

public class Person implements Cloneable{

private int age ;

private String name;

public Person(int age, String name) {

this.age = age;

this.name = name;

}

public Person() {}

public int getAge() {

return age;

}

public String getName() {

return name;

}

protected Object clone() throws CloneNotSupportedException {

return (Person)super.clone();

}

}

由于age是基本数据类型, 那么对它的拷贝没有什么疑议,直接将一个4字节的整数值拷贝过来就行。但是name是String类型的, 它只是一个引用, 指向一个真正的String对象,那么对它的拷贝有两种方式: 直接将源对象中的name的引用值拷贝给新对象的name字段, 或者是根据原Person对象中的name指向的字符串对象创建一个新的相同的字符串对象,将这个新字符串对象的引用赋给新拷贝的Person对象的name字段。这两种拷贝方式分别叫做浅拷贝和深拷贝。深拷贝和浅拷贝的原理如下图所示:

1107f2af60cb

3

下面通过代码进行验证。如果两个Person对象的name的地址值相同, 说明两个对象的name都指向同一个String对象, 也就是浅拷贝, 而如果两个对象的name的地址值不同, 那么就说明指向不同的String对象, 也就是在拷贝Person对象的时候, 同时拷贝了name引用的String对象, 也就是深拷贝。验证代码如下:

Person p = new Person(23, "zhang");

Person p1 = (Person) p.clone();

String result = p.getName() == p1.getName()

? "clone是浅拷贝的" : "clone是深拷贝的";

System.out.println(result);

打印结果为:clone是浅拷贝的

所以,clone方法执行的是浅拷贝, 在编写程序时要注意这个细节。

于是对于对象中包含对象属性时,我们需要手工覆盖原来的clone方法来达到深拷贝。

9、类的访问权限

|权限名|同一个类 |同一个包| 子类|所有类|

|----|----|----|----|

|private|true|false|false|false|

|default|true|true|false|false|

|protected|true|true|true|false|

|public|true|true|true|true|

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值