j2ee面试宝典翻译(3) j2ee job interview companion

本文深入探讨了面向对象编程中的继承与组合概念,解释了“是一个”与“有一个”的关系,并对比了组合与聚合的区别。此外,文章还介绍了多态、封装的概念及其实现方式,并通过示例代码展示了不同技术的应用。

Q9:如何让表达“是一个”和“有一个”关系?或者请解释下“继承”和“组合”。组合和聚合之间有什么区别?

A9:“是一个”的关系表示继承而“有一个”的关系是表示组合。继承和组合都允许你将子对象放入新类中。代码重用的两个主要技术便是类继承和对象组合。

继承是单向的。例如房子是一栋建筑,但建筑不是一个房子。继承使用extends关键字。

组合:用于表达房子有一个浴室。说房子是一个浴室就不准确了。组合简单地使用实例变量引用其他对象,如House类拥有一个实例变量,引用一个Bathroom对象。


Q:哪一个更好?组合还是继承?

A:指南是,仅当子类“是一个”父类时,才使用继承。

  • 不要仅仅为了代码重用而使用继承。如果没有“是一个”的关系,就应该使用组合语法。过度使用实现继承(使用“扩展”关键字)会破坏所有的子类,特别在超类需要被修改的时候。
  • 不要仅仅为了获得多态性而使用继承。如果没有“是一个”的关系,而你又想要获得多态性,那么,使用接口继承和组合语法。

Q:组合和聚合有什么区别?

A:它们都表达整体和局部的关系。聚合关系,局部可以独立于整体而存在。例如,一个行项目和产品是整体和局部的关系。如果一个行项目被删除,对应的产品不需要被删除。所以聚合是一种较弱的关系。组合关系,局部不可独立于整体而存在。如果一个整体被删除,那么所有零件也会被删除。例如,订单和项目是整体和局部的关系。如果一个订单被删除,那么相应的行项目也应该被删除。所以组合具有更强的关系。


Q10:你怎么理解继承、封装、多态和动态绑定?

A10:Polymorphism多态——描述了这样一种能力,一个给定类型的变量可以被用来引用多个类型不同的对象(当然,需要这些类型是给定类型的子类),而调用的却是这个变量引用的对象的具体类型上的方法。简而言之,多态是一种自下而上的方法调用。多态的好处是,很容易添加新的扩展类而不破坏原有的调用代码。当给一个对象发送一条消息(调用方法)时,你甚至不知道这个对象的具体类型,但是正确的行为会发生,这就是多态。

面向对象编程语言实现多态的过程称为动态绑定。(运行期类型推断。)

Inheritance继承——是将基类的行为(即方法)和状态(即变量)包含到派生类中,这样它们就可在派生类中被访问了。关键的好处是,它提供了代码重用的正式机制。

任何业务逻辑的公共部分都可以从派生类移至基类,重构时这样做,可以避免代码重复而提高代码的可维护性。

现有的类被称为基类而派生类被称为子类。继承也可以被定义为一个过程,即对象获得一个或多个其他对象的特征的过程,就像孩子从父母那里获得特征一样。

有两种类型的继承:

1、实作继承:可以通过继承部分或全部父类中已经实现的功能来扩展程序。在Java中,您只可以从一个超类继承。实作继承提升了重用性,但是不正确的继承使用可能导致编程噩梦,因为它会破坏封装性并且为将来的变化带来问题。使用实作继承,子类变得和父类紧密耦合起来。这将使得设计变得脆弱,如果你想改变父类,就不得不了解子类的细节以免破坏他们。所以使用实作继承,确保子类只依赖父类的行为,而不是实际的实现。

2、接口继承:接口提供了一种机制,将无关的类联系起来——通过指定系列普通方法,这些实现类都必须包含。(实现类之间可以是互不相关的。)接口继承提升了“面向接口编程而不是面向实现编程”的原则。这样降低了系统之间的耦合。在Java中,你可以实现任意数量的接口。这比“实作继承”更灵活,因为它不会把你锁定在具体实现中,具体实现会使子类变得难以维护。也要小心,修改接口会破坏实现类。

Which one to use?优先选择接口,因为它符合“面向接口编程”的理念并且可以降低耦合。接口继承可以在对象组合的帮助下实现代码的重用。如果你看GOF设计模式,你会发现他们更偏爱接口继承而不是实作继承。

实作继承案例:

package ch08_extends3;

/**
 * 假设活期存款和定期存款在存取行为上有类型的行为,我们把这两个行为的实现定义在父类中。
 * <p>但是活期存款和定期存款在计算利息这个行为上表现是不同的。
 *	@author zhengwei 2013-7-13
 */
public  abstract  class  Account   { 
    public void deposit (double amount) { 
          System.out.println("depositing " + amount); 
    } 
 
    public void withdraw (double amount) { 
          System.out.println ("withdrawing " + amount); 
    } 
     
    public abstract double calculateInterest (double  amount); 
} 
 
class SavingsAccount  extends Account  { 
 
     public double  calculateInterest  (double amount) { 
         // calculate interest for SavingsAccount 
         return amount * 0.03; 
     } 
     
     public void  deposit (double amount) { 
          super.deposit  (amount);  // get code reuse 
         // do something else 
     } 
 
     public void  withdraw (double amount) { 
         super.withdraw  (amount);  // get code reuse  
         // do something else 
     }   
} 
 
class TermDepositAccount extends Account  { 
 
    public double  calculateInterest (double amount) { 
        // calculate interest for SavingsAccount 
        return amount * 0.05; 
    } 
     
    public void deposit(double amount) { 
        super.deposit  (amount);  // get code reuse 
        // do something else 
    } 
 
    public void withdraw(double amount) { 
        super.withdraw  (amount); //  get code reuse 
        // do something else 
    } 
} 

接口继承案例:

package ch08_extends3;

/**
 * 接口继承示例代码,使用组合来重用代码。
 * <p>在下例中,deposite和withdraw方法共享了AccountHelper中的代码片段。
 * <p>而calculateInterest方法在各自实现中有独特的实现
 * @author zhengwei 2013-7-13
 */
public interface Account {
	public abstract double calculateInterest(double amount);

	public abstract void deposit(double amount);

	public abstract void withdraw(double amount);
}

interface AccountHelper {
	public abstract void deposit(double amount);

	public abstract void withdraw(double amount);
}

/**
 * class AccountHelperImpl has reusable code as methods deposit (double amount)
 * and withdraw (double amount). 
 * <p>AccountHelperImpl含有可重用代码:deposit方法和withdraw方法
 */
class AccountHelperImpl implements AccountHelper {
	public void deposit(double amount) {
		System.out.println("depositing " + amount);
	}

	public void withdraw(double amount) {
		System.out.println("withdrawing " + amount);
	}

}

class SavingsAccountImpl implements Account {
	// composed helper class (i.e. composition ).
	AccountHelper helper = new AccountHelperImpl();

	public double calculateInterest(double amount) {
		// calculate interest for SavingsAccount
		return amount * 0.03;
	}

	public void deposit(double amount) {
		helper.deposit(amount); // code reuse via composition
	}

	public void withdraw(double amount) {
		helper.withdraw(amount); // code reuse via composition
	}
}


class TermDepositAccountImpl implements Account {

	// composed helper class (i.e. composition ).
	AccountHelper helper = new AccountHelperImpl();

	public double calculateInterest(double amount) {
		// calculate interest for SavingsAccount
		return amount * 0.05;
	}

	public void deposit(double amount) {
		helper.deposit(amount); // code reuse via composition
	}

	public void withdraw(double amount) {
		helper.withdraw(amount); // code reuse via composition
	}

}

两种方式可以使用如下的测试代码:

package ch08_extends3;

/**
 * 
 * @author zhengwei 2013-7-13
 */
public class Test {
	public static void main(String[] args) {
		Account acc1 = new SavingsAccountImpl();
		acc1.deposit(50.0);

		Account acc2 = new TermDepositAccountImpl();
		acc2.deposit(25.0);

		acc1.withdraw(25);
		acc2.withdraw(10);

		double cal1 = acc1.calculateInterest(100.0);
		double cal2 = acc2.calculateInterest(100.0);

		System.out.println("Savings --> " + cal1);
		System.out.println("TermDeposit -->  " + cal2);
	}
}

输出结果:

depositing 50.0
depositing 25.0
withdrawing 25.0
withdrawing 10.0
Savings --> 3.0
TermDeposit -->  5.0

问:为什么优先通过组合来重用代码而不是继承?

答:可以看到两种方式都可利用多态,并重用了代码,结果也是一致的,但是:

  • 类继承的优点是,它的重用是在编译时静态地完成的,是易于使用的。类继承的缺点也是因为它是静态的,从父类继承而来的实现在运行期不能被改变。而在对象组合中,(组合进来的)功能是在运行期动态获得的,通过对象收集其他对象的引用来达成。这种方法的优点是,组合进来的“实作对象”在运行时是可以更换的。这是因为我们依赖的是对象的接口类型,调用对象也只有通过他们的接口,所以一个对象可以被替换为另一个,只要他们有相同的类型(接口)。例如:组合进来的类型AccountHelperImpl可以在有需要时被替换为一个更有效率的实现:

public class  EfficientAccountHelperImpl  implements AccountHelper  { 
    public void  deposit(double amount) { 
        System.out.println(" efficient depositing " + amount); 
    } 
 
    public void withdraw(double amount) { 
        System.out.println(" efficient withdrawing " + amount); 
    } 
}

译注:感觉这里没说透。我来说下这个问题,“父母是不可以动态替换的,但是朋友可以是动态替换的”。一旦继承了某类,你不可能替换这种继承关系,但是组合,我们可以通过向构造器传参或者setter方法临时改变组装进来的对象的类型,当然前提是这些对象的类型否和依赖的接口类型。

再进一步,重用代码要么用super.someMethod(),这个super指向父类对象,这个super你是没法换的。要么是通过brother.someMethod()重用代码,这个brother是组合语法中的域成员,它指向和我们协作的,或者说依赖的对象,这种对象只需一个set方法就可替换了。

  • 另一个问题是,实作继承中,子类依赖父类实现。这使得子类难以被重用,特别是继承而来的实作不再令人满意并因此而破坏封装性(译注:没看懂!)另外,对父类的修改不仅会沿着继承层次影响子类,还会影响到单纯使用子类的其他代码,这种子类严重耦合父类的设计是非常脆弱的。但是改变组合对象的接口/实现是容易的。

还是我上面说的那点。

因为对象组合的灵活性和强大,大部分设计模式只要有可能,就优先强调对象组合而非继承。很多时候,一个设计模式使用组合就展示了一个聪明的办法来解决一类常见问题,而不是用标准的、不那么灵活的基于继承的解决方案。

Encapsulation封装——指的是保持所有相关成员(变量和方法)在一起,在一个对象中。指定成员变量为私有可以隐藏变量和方法。对象应该向外界隐藏他们的内部运作。好的封装提高代码模块化,通过防止对象以一种意想不到的方式相互作用,从而使未来的开发和重构工作更容易。

示例代码:

class MyMarks {
	private int vmarks = 0;
	private String name;

	public void setMarks(int mark) throws MarkException {
		if (mark > 0)
			this.vmarks = mark;
		else {
			throw new MarkException("No negative Values");
		}
	}

	public int getMarks() {
		return vmarks;
	}
	// getters and setters for attribute name goes here.
}

能够封装类的成员对于安全性和完整性来说是极其重要的。我们可以保护变量接收不合法的值。上面的示例代码描述了如何通过封装来保护MyMarks不拥有负值。任何修改成员变量”vmarks”的行为必须通过setter方法setMarks(int)。这可以防止对象”MyMarks”拥有负值,调用者传入负值将得到一个异常。


J2EE高级培训师面试题 时间:120分钟 一,填空题 1) 每个Servlet都必须实现()接口 a) javax.servlet.Servlet; b) javax.servlet.HttpServlet; c) javax.servlet.GenericServlet; d) javax.servlet.http.Servlet; 2) 利用JavaMail API发送邮件的过程中,()能获取正确的session对象。 a) Session mailsession=Session.getInstance(); b) Session mailsession=Session.getInstance(objProperties); c) Session mailsession = request.getSession(); d) Session mailsession= request.getInstance(objProperties); 参考答案 b 3) 下面是一JSP页面的代码,请问输出结果是().  function display(){  if(i == 1) {      alert("Is 1");    } else if(i==2) {  alert("Is 2"); }else{      alert("Is other");    }  }  display (); a) 什么也不输出。 b) 弹出内容为“Is 1”的对话框。 c) 弹出内容为“Is 2”的对话框。 d) 当第一次访问这个程序时,弹出一个内容为"Is other"的对话框,从第二次开始,每次访问这个程序都弹出内容为“Is 2”的对话框。 e 当第一次访问这个程序时,弹出一个内容为"Is 1"的对话框,从第二次开始,每次访问这个程序都弹出内容为“Is 2”的对话框。 参考答案 a 4) 下面是一JSP页面的片断代码,请问页面输出结果是(). a) Hello b) World c) HelloWorld d) 什么也不输出 参考答案 d 5) aa.jsp文件如下: <% for(;I++ 两个客户依次使用浏览器浏览aa.jsp,且每个客户只浏览一次,那么,第2个客户的浏览器将显示什么? e a) 0 1 b) 1 2 c) 3 4 d) 5 6 e) 什么也没有 6) 如果用JSP开发一个聊天程序,不用数据库存储聊天纪录,请问聊天记录最好存储在()中。 a) request b) page c) session d) application 参考答案 d 7) 在web.xml中,有如下代码: 30 对于以下描述,正确的是: b a) 定义了默认的会话超时时长。时长为30秒。 b) 可以使用HttpSession接口的getMaxInactiveInterval()方法把该值取出来。 c) 定义了默认的会话超时时长。时长为30小时。 d) 可以使用Session类的getMaxInactiveInterval()方法把该值取出来。 8) 对于标记,描述不正确的是() a) 一般来说,是、标记的子标记。 b) 如果aa.jsp有代码:,则在next.jsp中可以使用request.getParameter("name");把属性name的值取出来。 c) 如果aa.jsp有代码:,则在next.jsp中可以使用request.getAttribute("name");把属性name的值取出来。 d) 如果标记放在标记外,也就是不作为标记的子标记,则使用浏览器查看时会显示错误页面。 9) aa.jsp与bb.jsp属于同一应用,在aa.jsp中使用标记,在bb.jsp中要把beanName这个bean对象变量所引用的对象取出来。那么对于以下描述,正确的是: c a) 在bb.jsp中仅使用代码:mybean bc = session.getAttribute("beanName"); 取出beanName所引用的对象。 b) 在bb.jsp中取beanName所引用的对象前,先要使用导入mypackage.mybean类。 c) 在bb.jsp中,对于使用session的getAttribute方法取出的beanName所引用的对象,要进行强制类型转换。 d) 在bb.jsp中使用session的getAttribute方法取出的对象和aa.jsp中的beanName所引用的对象无关。 10) 用JSP2.0表达式语法在JSP页面上正确显示10+20的结果的是()。 a) ${10+20} b) ${10+20}; c) $(10+20) d) $(10+20); 参考答案 a 11) 编写自定义标签处理类后,需要编写一个()去描述。 a) .tag 文件 b) .tld 文件 c) .dtd文件 d) .xml 文件 12) 关于Tag File说法不正确的是()。 a) Tag File是JSP2.0新增的功能 b) Tag File是JSP1.2增的功能 c) Tag File 可以让网页开发人员直接使用JSP语法制作标签 d) Tag File的扩展名可以 .tag 参考答案 b 13) 关于Servlet Filter,下列说法正确的有()。 a) Filter 其实就是一个Servlet b) Filter 可以产生response c) Filter可以在servlet被调用之前截获request d) Filter可以用来处理统一认证,过滤不雅字句等。 参考答案 c,d 14) 会话Bean的状态的说法正确的有(a)。 a) 有状态会话Bean的状态是借助于序列化保存下来的 b) 有状态会话Bean被激活后会恢复挂起前的状态 c) 无状态会话Bean不能有属性 d) 有状态会话Bean的所有属性在挂起的时候都会被保存下来 参考答案 15) 关于EJB组成部分的说法不正确的是 (ab) a) 每一个EJB的实例对象对应于一个JNDI名字,通过JNDI名字找到相应的实例 b) 所有EJB都包含Home接口,远程接口,Bean类三个部分 c) Bean类必须实现远程接口中的方法 d) 远程接口实现了RMI的Remote接口 参考答案 16) 下列关于RMI远程接口定义正确的是 d a) public interface Demo {public long test() throws java.rmi.RemoteException;} b) Public interface Demo extends Remote {public long test() } c) interface Demo extends Remote {public long test() throws java.rmi.RemoteException;} d) public interface Demo extends Remote {public long test() throws java.rmi.RemoteException;} 参考答案 17) 下列选项中能获得UserTransaction的是ab a) 通过上下文的.getUserTransaction()方法拿到UserTransaction b) 通过查找JNDI名为javax.transaction.UserTransaction的拿到UserTransaction c) new 一个UserTransaction对象 d) 不需要实例化,直接使用接口中的方法即可 参考答案 18) 关于事务,下列说法不正确的事 a a) EJB支持平面式和嵌入式两种事务模型 b) 平面式事务模型是指要么操作都成功,如果失败,所有操作都回滚 c) EJB有Bean管理事务和容器管理事务两种方式 d) BMT方式也可以使用JTA来控制事务边界 参考答案 19) 关于消息确认描述正确的是 ac a) 在创建消息会话的时候能指定消息的确认方式 b) AUTO_ACKNOWLEDGE方式能确认之前收到的所有消息 c) Client_ACKNOWLEDGE方式需要显示调用方法ACKNOWLEDGE方法来显式的确认消息 d) Client_ACKNOWLEDGE方式可以确认所有的消息 参考答案 20) 关于消息Bean事务描述正确的是 c a) MDB支持RequiresNew事务方式 b) MDB支持Mandatory事务方式 c) NotSupported方式在容器回调OnMessage方法之后才启动事务 d) MDB不支持Required事务属性 参考答案 21) 关于WSDL不正确的说法是 bc a a) 用来描述Web服务的XML文件 b) 全称是WEB SERVICES DEFINITION LANGUAGE c) 客户端调用时直接用WSDL文件中的地址就可以了 d) SOAP协议可以基于多种底层协议 二,简答题 1:简述四种会话跟踪技术以及他们的优缺点 2:请结合轻量级框架Struts,Spring和重量级框架EJB谈谈轻量级架构和重量级架构的区别? 3:说说你所熟悉或听说过的j2ee中的几种常用模式?及对设计模式的一些看法 4:如何给weblogic指定大小的内存?如何设定的weblogic的热启动模式(开发模式)与产品发布模式? 三,代码题 1:请写出JavaMail发送邮件的关键代码 2:设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。 3:用冒泡法对10个数排序(由小到大)例如: 54,12,-6,6,22,-7,9,0,999,79 4:有一个登录页面,上面有用户名(name),密码(password)两个字段,对应数据库表User中的两个字段,请用Struts+EJB实现上述登陆场景!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值