java基础学习(后)

53、StringString StringBuffer和StringBuilder 的区别是什么?

String是只读字符串,它并不是基本数据类型,而是一个对象。从底层源码来看是一个final类型的字符数组,所引用的字符串不能被改变,一经定义,无法再增删改。每次对String的操作都会生成新的String对象

private final char value[];

每次+操作 : 隐式在堆上new了一个跟原字符串相同的StringBuilder对象,再调用append方法 拼接+后面的字符

StringBuffer与StringBuilder都继承了AbstractStringBulder类,而AbtractStringBuilder又实现了CharSequence接口,两个类都是用来进行字符串操作的。在做字符串拼接修改删除替换时,效率比string更高。

StringBuffer是线程安全的,Stringbuilder是非线程安全的。所以Stringbuilder比stringbuffer效率更高,StringBuffffer的方法大多都加了synchronized关键字

54、String str=”aaa”,与String str=new String(“aaa”)一样吗?

String x = "张三";
String y = "张三";
String z = new String("张三");
System.out.println(x == y); // true
System.out.println(x == z); // false

为什么会输出上边的结果呢,String x = “张三” 的方式,Java 虚拟机会将其分配到常量池中,而常量池中没有重复的元素,比如当执行“张三”时,java虚拟机会先在常量池中检索是否已经有“张三”,如果有那么就将“张三”的地址赋给变量,如果没有就创建一个,然后在赋给变量;而 String z = new String(“张三”) 则会被分到堆内存中,即使内容一样还是会创建新的对象。

public class StringTest {
	public static void main(String[] args){
		String s1="Hello";
		String s2="Hello";
		String s3=new String("Hello");
		System.out.println("s1和s2 引用地址是否相同:"+(s1 == s2));
		System.out.println("s1和s2 值是否相同:"+s1.equals(s2));
		System.out.println("s1和s3 引用地址是否相同:"+(s1 == s3));
		System.out.println("s1和s3 值是否相同:"+s1.equals(s3));
	}
}
s1和s2 引用地址是否相同:true
s1和s2 值是否相同:true
s1和s3 引用地址是否相同:false
s1和s3 值是否相同:true

上面程序中的"==“是判断两个对象引用的地址是否相同,也就是判断是否为同一个对象,s1与s2 返回为true,s1与s3返回则是false。说明s1与s2 引用的同一个对象的地址,s3则与其它两个引用不是同一个对象地址。
Java为了避免产生大量的String对象,设计了一个字符串常量池。工作原理是这样的,创建一个字符串时,JVM首先为检查字符串常量池中是否有值相等的字符串,如果有,则不再创建,直接返回该字符串的引用地址,若没有,则创建,然后放到字符串常量池中,并返回新创建的字符串的引用地址。所以上面s1与s2引用地址相同。
那为什么s3与s1、s2引用的不是同一个字符串地址呢? String s3=new String(“Hello”); JVM首先是在字符串常量池中找"Hello” 字符串,如果没有创建字符串常量,然后放到常量池中,若已存在,则不需要创建;当遇到 new 时,还会在内存(不是字符串常量池中,而是在堆里面)上创建一个新的String对象,存储"Hello",并将内存上的String对象引用地址返回,所以s3与s1、s2引用的不是同一个字符串地址。

s1与s2指向的都是常量池中的字符串常量,所以它们比较的是同一块内存地址,而s3指向的是堆里面的一块地址,说的具体点应该是堆里面的Eden区域,s1跟s3,s2跟s3比较都是不相等的,都不是同一块地址。

了解了String类的工作原理,回归问题本身:
在String的工作原理中,已经提到了,new一个String对象,是需要先在字符串常量中查找相同值或创建一个字符串常量(有则用没有则创建),然后再在内存中创建一个String对象,所以String str = new String(“xyz”); 会创建一个或两个对象

55、讲下java中的math类有那些常用方法?

  • Pow():幂运算
  • Sqrt():平方根
  • Round():四舍五入
  • Abs():求绝对值
  • Random():生成一个0-1的随机数,包括0不包括1

56、String类的常用方法有那些?

  • charAt:返回指定索引处的字符
  • indexOf():返回指定字符的索引
  • replace():字符串替换
  • trim():去除字符串两端空白
  • split():分割字符串,返回一个分割后的字符串数组
  • getBytes():返回字符串的byte类型数组
  • length():返回字符串长度
  • toLowerCase():将字符串转成小写字母
  • toUpperCase():将字符串转成大写字符
  • substring():截取字符串
  • format():格式化字符串
  • equals():字符串比较

57、Java中的继承是单继承还是多继承

Java中既有单继承,又有多继承。对于java类来说只能有一个父类,对于接口来说可以同时继承多个接口

58、Super与this表示什么?

Super表示当前类的父类对象

This表示当前类的对象

59、普通类与抽象类有什么区别?

普通类不能包含抽象方法,抽象类可以包含抽象方法

抽象类不能直接实例化,普通类可以直接实例化

60、什么是接口?为什么需要接口?

接口就是某个事物对外提供的一些功能的声明,是一种特殊的java类,接口弥补了java单继承的缺点

61、接口有什么特点?

  1. 接口中声明全是public static fifinal修饰的常量
  2. 接口中所有方法都是抽象方法
  3. 接口是没有构造方法的
  4. 接口也不能直接实例化
  5. 接口可以多继承

62、抽象类和接口的区别?

抽象类:
  1. 抽象方法,只有行为的概念,没有具体的行为实现。使用abstract关键字修饰,没有方法体。子类必须重写这些抽象方法。

  2. 包含抽象方法的类,一定是抽象类。

  3. 抽象类只能被继承,一个类只能继承一个抽象类。

接口:
  1. 全部的方法都是抽象方法,属性都是常量

  2. 不能实例化,可以定义变量。

  3. 接口变量可以引用具体实现类的实例

  4. 接口只能被实现,一个具体类实现接口,必须实现全部的抽象方法

  5. 接口之间可以多实现

  6. 一个具体类可以实现多个接口,实现多继承现象

63、Hashcode的作用

java的集合有两类,一类是List,还有一类是Set。前者有序可重复,后者无序不重复。当我们在set中插入的时候怎么判断是否已经存在该元素呢,可以通过equals方法。但是如果元素太多,用这样的方法就会比较满。

于是有人发明了哈希算法来提高集合中查找元素的效率。 这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储的那个区域

hashCode方法可以这样理解:它返回的就是根据对象的内存地址换算出的一个值。这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

64、 Java的四种引用,强弱软虚

强引用

强引用是平常中使用最多的引用,强引用在程序内存不足(OOM)的时候也不会被回收,使用方式:

String str=new String(“aaa”)
软引用

软引用在程序内存不足时,会被回收,使用方式:

// 注意:wrf这个引用也是强引用,它是指向SoftReference这个对象的, 
// 这里的软引用指的是指向new String("str")的引用,也就是SoftReference类中T 
SoftReference<String> wrf =new SoftReference<String>(new String(“aaa”))

可用场景: 创建缓存的时候,创建的对象放进缓存中,当内存不足时,JVM就会回收早先创建的对象。

弱引用

弱引用就是只要JVM垃圾回收器发现了它,就会将之回收,使用方式:

WeakReference<String>wrf=newWeakReference<String>(str)

可用场景:Java源码中的java.util.WeakHashMap中的key就是使用弱引用,我的理解就是,

一旦我不需要某个引用,JVM会自动帮我处理它,这样我就不需要做其它操作。

虚引用

虚引用的回收机制跟弱引用差不多,但是它被回收之前,会被放入ReferenceQueue中。注意哦,其它引用是被JVM回收后才被传入ReferenceQueue中的。由于这个机制,所以虚引用大多被用于引用销毁前的处理工作。还有就是,虚引用创建的时候,必须带有ReferenceQueue,使用例子:

PhantomReference<String>prf=newPhantomReference<String>(new String("str"),newReferenceQueue<>());

可用场景: 对象销毁前的一些操作,比如说资源释放等。 Object.finalize() 虽然也可以做这类动作,但是这个方式即不安全又低效

上诉所说的几类引用,都是指对象本身的引用,而不是指 Reference 的四个子类的引用( SoftReference 等)。

65、Java创建对象有几种方式?

java中提供了以下四种创建对象的方式:

  1. new创建新对象

  2. 通过反射机制

  3. 采用clone机制

  4. 通过序列化机制

66、有没有可能两个不相等的对象有相同的hashcode

有可能.在产生hash冲突时,两个不相等的对象就会有相同的 hashcode 值.当hash冲突产生时,一般有以下几种方式来处理:

  1. 拉链法:每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用这个单向链表进行存储.

拉链法

  1. 开放定址法:一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入

  2. 再哈希:又叫双哈希法,有多个不同的Hash函数.当发生冲突时,使用第二个,第三个….等哈希函数计算地址,直到无冲突.

67、拷贝和浅拷贝的区别是什么?

浅拷贝:

被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象.

深拷贝:

被复制对象的所有变量都含有与原来的对象相同的值.而那些引用其他对象的变量将指向被复制过的新对象.而不再是原有的那些被引用的对象.换言之.深拷贝把要复制的对象所引用的对象都复制了一遍.

68、static都有哪些用法?

所有的人都知道static关键字这两个基本的用法:静态变量和静态方法.也就是被static所修饰的变量/方法都属于类的静态资源,类实例所共享.除了静态变量和静态方法之外,static也用于静态块,多用于初始化操作:

public calss PreCache{ 

    static{ 

    //执行相关操作 

    } 
}

此外static也多用于修饰内部类,此时称之为静态内部类.

最后一种用法就是静态导包,即 import static .import static是在JDK 1.5之后引入的新特性,可以用来指定导入某个类中的静态资源,并且不需要使用类名,可以直接使用资源名,比如:

import static java.lang.Math.*; 

public class Test{ 

	public static void main(String[] args){ 

	//System.out.println(Math.sin(20));传统做法 

	System.out.println(sin(20)); 

	} 

}

69、a=a+b与a+=b有什么区别吗?

+= 操作符会进行隐式自动类型转换,此处a+=b隐式的将加操作的结果类型强制转换为持有结果的类型, 而a=a+b则不会自动进行类型转换.

如:

byte a = 127; 
byte b = 127; 
b = a + b; // 报编译错误:cannot convert from int to byte 
b += a; 

以下代码是否有错,有的话怎么改?

short s1= 1; 
s1 = s1 + 1;

有错误.short类型在进行运算时会自动提升为int类型,也就是说 s1+1 的运算结果是int类型,而s1是short

类型,此时编译器会报错.

正确写法:

short s1= 1; 
s1 += 1;

+=操作符会对右边的表达式结果强转匹配左边的数据类型,所以没错.

70、final、finalize()、finally

性质不同
  1. final为关键字;

  2. finalize()为方法;

  3. finally为区块标志,用于try语句中;

作用
  1. final为用于标识常量的关键字,final标识的关键字存储在常量池中

  2. finalize()方法在Object中进行了定义,用于在对象“消失”时,由JVM进行调用用于对对象进行垃圾回收,类似于C++中的析构函数;用户自定义时,用于释放对象占用的资源(比如进行I/0操作);

  3. finally{}用于标识代码块,与try{}进行配合,不论try中的代码执行完或没有执行完(这里指有异常),该代码块之中的程序必定会进行;

71、JDBC操作的步骤

  1. 加载数据库驱动类
  2. 打开数据库连接
  3. 执行sql语句
  4. 处理返回结果
  5. 关闭资源

72、在使用jdbc的时候,如何防止出现sql注入的问题。

使用PreparedStatement类,而不是使用Statement类

73、怎么在JDBC内调用一个存储过程

使用CallableStatement

74、是否了解连接池,使用连接池有什么好处?

数据库连接是非常消耗资源的,影响到程序的性能指标。

连接池是用来分配、管理、释放数据库连接的,可以使应用程序重复使用同一个数据库连接,而不是每次都创建一个新的数据库连接。通过释放空闲时间较长的数据库连接避免数据库因为创建太多的连接而造成的连接遗漏问题,提高了程序性能。

75、你所了解的数据源技术有那些?使用数据源有什么好处?

Dbcp,c3p0等,用的最多还是c3p0,因为c3p0比dbcp更加稳定,安全;通过配置文件的形式来维护数据库信息,而不是通过硬编码。当连接的数据库信息发生改变时,不需要再更改程序代码就实现了数据库信息的更新

76、&和&&的区别

&是位运算符。&&是布尔逻辑运算符,在进行逻辑判断时用&处理的前面为false后面的内容仍需处理,用&&处理的前面为false不再处理后面的内容。

77、静态内部类如何定义

定义在类内部的静态类,就是静态内部类。

public class Out { 
    private static int a; 
    private int b;
    public static class Inner { 
                   public void print() { 
                       System.out.println(a); 
                   } 
               } 
}
  1. 静态内部类可以访问外部类所有的静态变量和方法,即使是 private 的也一样。

  2. 静态内部类和一般类一致,可以定义静态变量、方法,构造方法等。

  3. 其它类使用静态内部类需要使用“外部类.静态内部类”方式,如下所示:Out.Inner inner = new Out.Inner();inner.print();

  4. Java集合类HashMap内部就有一个静态内部类Entry。Entry是HashMap存放元素的抽象,HashMap 内部维护 Entry 数组用了存放元素,但是 Entry 对使用者是透明的。像这种和外部类关系密切的,且不依赖外部类实例的,都可以使用静态内部类。

78、什么是成员内部类

定义在类内部的非静态类,就是成员内部类。成员内部类不能定义静态方法和变量(fifinal修饰的除外)。这是因为成员内部类是非静态的,类初始化的时候先初始化静态成员,如果允许成员内部类定义静态变量,那么成员内部类的静态变量初始化顺序是有歧义的。实例:

public class Out { 
    private static int a; 
    private int b;
    public class Inner { 
        public void print() { 
            System.out.println(a); 
            System.out.println(b); 
        } 
    } 
}

79、Static Nested Class 和 Inner Class的不同

Nested Class (一般是C++的说法),Inner Class (一般是JAVA的说法)。Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用上。注: 静态内部类(Inner Class)意味着1创建一个static内部类的对象,不需要一个外部类对象2不能从一个static内部类的一个对象访问一个外部类对象

80、什么时候用assert

assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为true;如果该值为false,说明程序已经处于不正确的状态下,系统将给出警告或退出。一般来说,assertion用于保证程序最基本、关键的正确性。assertion检查通常在开发和测试时开启。为了提高性能,在软件发布后,assertion检查通常是关闭的

81、Java有没有goto

java中的保留字,现在没有在java中使用

82、数组有没有length()这个方法? String有没有length()这个方法

数组没有length()这个方法,有length的属性。String有有length()这个方法

83、用最有效率的方法算出2乘以8等於几

2 << 3

84、float型float f=3.4是否正确?

不正确。精度不准确,应该用强制类型转换,如下所示:float f=(float)3.4;

85、排序都有哪几种方法?请列举

排序的方法有:

插入排序,冒泡排序,堆排序,归并排序,选择排序,计数排序,基数排序,桶排序,快速排序等

package com.itheima;
 
/**
 * 4、 排序有哪几种方法?请列举。并用JAVA实现一个快速排序.
 * 排序的方法有:冒泡排序、快速排序、选择排序、插入排序。。。
 * 
 */
 
public class Test4 {
 
	static int count = 0;
	
	public static void main(String[] args) {
		int values[] = { 5, 4, 8, 3, 7, 2, 1, 9, 0, 6 };
 
		qsort(values, 0, (values.length - 1));
 
		System.out.printf("\n\n排序后的结果是:");
		for (int i = 0; i < values.length; i++) {
			System.out.printf("%d ", values[i]);
		}
	}
 
	public static void qsort(int values[], int left, int right) {
		int tmp = 0;
 
		System.out.printf("\n这个是第%d次排序的结果:", count);
		count++;
		for (int i = 0; i < values.length; i++) {
			System.out.printf("%d ", values[i]);
		}
		
		if (left < right) {
			tmp = partition(values, left, right);
			qsort(values, left, tmp);
			qsort(values, tmp + 1, right);
		}
	}
 
	public static int partition(int values[], int left, int right) {
		int i = 0, j = 0;
		int key = 0, tmp = 0;
 
		if (null == values) {
			return 0;
		}
 
		i = left;
		j = right;
		key = values[left];
 
		// 这个while循环可以实现排序的第一步:分组
		while (i < j) {
			while (values[j] > key) {
				--j;
			}
			tmp = values[i];
			values[i] = values[j];
			values[j] = tmp;
 
			while (values[i] < key) {
				i++;
			}
			tmp = values[i];
			values[i] = values[j];
			values[j] = tmp;
		}
 
		return i;
 
	}
}

87、说出一些常用的类,包,接口,请各举5个

常用的类:BufffferedReader BufffferedWriter FileReader FileWirter String Integer

常用的包:java.lang java.awt java.io java.util java.sql

常用的接口:Remote List Map Document NodeList

88、a.hashCode()有什么用?与 a.equals(b) 有什么关系?

hashCode() 方法是相应对象整型的 hash 值。它常用于基于 hash 的集合类,如 Hashtable、HashMap、LinkedHashMap 等等。它与equals() 方法关系特别紧密。根据 Java 规范,两个使用 equal() 方法来判断相等的对象,必须具有相同的 hash code。

89、Java 中的编译期常量是什么?使用它又什么风险?

公共静态不可变(public static final )变量也就是我们所说的编译期常量,这里的 public 可选的。实际上这些变量在编译时会被替换掉,因为编译器知道这些变量的值,并且知道这些变量在运行时不能改变。这种方式存在的一个问题是你使用了一个内部的或第三方库中的公有编译时常量,但是这个值后面被其他人改变了,但是你的客户端仍然在使用老的值,甚至你已经部署了一个新的 jar。为了避免这种情况,当你在更新依赖 JAR 文件时,确保重新编译你的程序

91、构造器(constructor)是否可被重写(override)?

构造器不能被继承,因此不能被重写,但可以被重载。

92、两个对象值相同(x.equals(y) == true),但却可有不同的hashcode,这句话对不对?

不对,如果两个对象 x 和 y 满足 x.equals(y) == true,它们的哈希码(hash code)应当相同。Java 对于 eqauls 方法和 hashCode 方法是这样规定的:

(1)如果两个 对象相同(equals 方法返回 true),那么它们的 hashCode 值一定要相同;

(2)如果两个对象的 hashCode 相同,它们并不一定相同。当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在 Set 集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。

93、是否可以继承String 类?

String 类是 final 类,不可以被继承,继承 String 本身就是一个错误的行为,对 String 类型最好的重用方式是关

联关系(Has-A)和依赖关系(Use-A)而不是继承关系(Is-A)。

94、当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?

值传递。Java 语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。C++和 C#中可以通过传引用或传输出参数来改变传入的参数的值。在 C#中可以编写如下所示的代码,但是在 Java 中却做不到。

说明:Java 中没有传引用实在是非常的不方便,这一点在 Java 8 中仍然没有得到改进,正是如此在 Java 编写的代码中才会出现大量的Wrapper 类(将需要通过方法调用修改的引用置于一个 Wrapper 类中,再将 Wrapper 对象传入方法),这样的做法只会让代码变得臃肿,尤其是让从 C 和 C++转型为 Java 程序员的开发者无法容忍。

96、重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?

方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求。

97、char型变量中能不能存贮一个中文汉字,为什么?

char 类型可以存储一个中文汉字,因为 Java 中使用的编码是 Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个 char 类型占 2 个字节(16 比特),所以放一个中文是没问题的。

补充:使用 Unicode 意味着字符在 JVM 内部和外部有不同的表现形式,在 JVM内部都是 Unicode,当这个字符被从 JVM 内部转移到外部时(例如存入文件系统中),需要进行编码转换。所以 Java 中有字节流和字符流,以及在字符流和字节流之间进行转换的转换流,如InputStreamReader 和 OutputStreamReader,这两个类是字节流和字符流之间的适配器类,承担了编码转换的任务;对于 C 程序员来说,要完成这样的编码转换恐怕要依赖于 union(联合体/共用体)共享内存的特征来实现了。

98、抽象类(abstract class)和接口(interface)有什么异同 ?

抽象类和接口都不能够实例化,但可以定义抽象类和接口类型的引用。一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类。接口比抽象类更加抽象,因为抽象类中可以定义构造器,可以有抽象方法和具体方法,而接口中不能定义构造器而且其中的方法全部都是抽象方法。抽象类中的成员可以是 private、默认、protected、public 的,而接口中的成员全都是 public 的。抽象类中可以定义成员变量,而接口中定义的成员变量实际上都是常量。有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法。

100、Java 中会存在内存泄漏吗,请简单描述。

理论 上 Java 因为 有垃 圾回 收机 制( GC)不 会存 在内 存泄 露问 题( 这也 是 Java 被广泛 使用 于服 务器 端编 程的 一个 重要 原因 );然而 在实 际开 发中 ,可 能会 存在 无用但 可达 的对 象,这些 对象 不能 被 GC 回收 ,因此 也会 导致 内存 泄露 的发 生 。

例 如Hibernate 的 Session( 一级 缓存 )中的 对象 属于 持久 态,垃圾 回收 器是 不会 回收这些 对象 的,然而 这些 对象 中可 能存 在无用的 垃圾 对象 ,如果 不及 时关 闭(close)或清 空( flflush)一 级缓 存就 可能 导致 内存 泄露 。下 面例 子中 的代 码也 会导 致内 存泄露

public class MyStack<T> { 
    private T[] elements; 
    private int size = 0; 
    private static final int INIT_CAPACITY = 16; 
    public MyStack() { 
        elements = (T[]) new Object[INIT_CAPACITY]; 
    }
    public void push(T elem) { 
        ensureCapacity(); 
        elements[size++] = elem; 
    }
    public T pop() {
        if(size == 0) throw new EmptyStackException(); 
        return elements[--size];
    }
    private void ensureCapacity() {
        if(elements.length == size) {
            elements = Arrays.copyOf(elements, 2 * size + 1); 
        } 
    }
}

上面的代码实现了一个栈(先进后出(FILO))结构,乍看之下似乎没有什么明显的问题,它甚至可以通过你编写的各种单元测试。然而其中的 pop 方法却存在内存泄露的问题,当我们用 pop 方法弹出栈中的对象时,该对象不会被当作垃圾回收,即使使用栈的程序不再引用这些对象,因为栈内部维护着对这些对象的过期引用(obsolete reference)。在支持垃圾回收的语言中,内存泄露是很隐蔽的,这种内存泄露其实就是无意识的对象保持。如果一个对象引用被无意识的保留起来了,那么垃圾回收器不会处理这个对象,也不会处理该对象引用的其他对象,即使这样的对象只有少数几个,也可能会导致很多的对象被排除在垃圾回收之外,从而对性能造成重大影响,极端情况下会引发Disk Paging(物理内存与硬盘的虚拟内存交换数据),甚至造成 OutOfMemoryError。

101、抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被synchronized修饰?

都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如 C 代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized 和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。

102、是否可以从一个静态(static)方法内部发出对非静态(non-static)方法的调用?

不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,在调用静态方法时可能对象并没有被初始化。

103、如何实现对象克隆?

有两种方式:

1). 实现 Cloneable 接口并重写 Object 类中的 clone()方法;

2). 实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真

正的深度克隆,代码如下。

测试代码:

public class MyUtil { 
    private MyUtil() { 
        throw new AssertionError();
    }
    @SuppressWarnings("unchecked") 
    public static <T extends Serializable> T clone(T obj) throws Exception { 
        ByteArrayOutputStream bout = new ByteArrayOutputStream(); 
        ObjectOutputStream oos = new ObjectOutputStream(bout); 
        oos.writeObject(obj); ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bin);
        return (T) ois.readObject();
        // 说明:调用 ByteArrayInputStream ByteArrayOutputStream对象的 close 方法没有任何意义 
        // 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这一点不同于对外部资源(如文件流)的释放 
    } 
}
class Person implements Serializable {
    private static final long serialVersionUID = -9102017020286042305L; 
    private String name; // 姓名 
    private int age; // 年龄 
    private Car car; // 座驾 
    public Person(String name, int age, Car car) { 
        this.name = name; 
        this.age = age; 
        this.car = car; 
    }
    public String getName() { 
        return name; 
    }
    public void setName(String name) {
        this.name = name;
                                      
    }
    public int getAge() { 
        return age; 
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Car getCar() {
        return car; 
    }public void setCar(Car car) { 
        this.car = car; 
    }
    @Override 
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", car=" + car + "]"; 
    } 
}
class Car implements Serializable {
 	 private static final long serialVersionUID = -5713945027627603702L;
	 private String brand; // 品牌 
     private int maxSpeed; // 最高时速 
     public Car(String brand, int maxSpeed) { 
         this.brand = brand; 
         this.maxSpeed = maxSpeed;
     }


	public String getBrand(String brand) { 
    	return brand; 
	}

	public void setBrand(String brand) { 
    	this.brand = brand; 
	}
	public int getMaxSpeed() { 
    return maxSpeed; 
	}
	public void setMaxSpeed(int maxSpeed) {
    	this.maxSpeed = maxSpeed; 
	}
	@Override 
	public String toString() { 
    	return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]"; 
	} 
}
class CloneTest {
    public static void main(String[] args) { 
        try { 
            Person p1 = new Person("Hao LUO", 33, new Car("Benz", 300)); 
            Person p2 = MyUtil.clone(p1); // 深度克隆 
            p2.getCar().setBrand("BYD"); // 修改克隆的 Person 对象 p2 关联的汽车对象的品牌属性 
            // 原来的 Person 对象 p1 关联的汽车不会受到任何影响 
            // 因为在克隆 Person 对象时其关联的汽车对象也被克隆了 
            System.out.println(p1); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 
    }
}

注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆, 更重要的是通过泛型限定, 可以检查出要克隆的对象是否支持序列化 ,这项检查是编译器完成的 ,不是在运行时抛出异常 ,这种是方案明显优于使用Object 类的 clone 方法克隆对象。 让问题在编译的时候暴露出来总是好过把问题留到运行时。

104、接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concreteclass)?

接 口 可 以 继 承 接 口 , 而 且 支 持 多 重 继 承 。 抽 象 类 可 以 实 现 (implements)接 口 , 抽 象 类 可 继 承 具 体 类 也 可 以 继 承 抽 象 类 。

105、一个”.java”源文件中是否可以包含多个类(不是内部类)?有什么限制?

可以,但一个源文件中最多只能有一个公开类(public class)而且文件名必须和公开类的类名完全保持一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>