程式語言面試常用題
1 .虛擬函式
虛擬函式(impure virtual),C++的虛擬函式主要作用是“執行時多型”,父類中提供虛擬函式的實現,為子類提供預設的函式實現。子類可以重寫父類的虛擬函式實現子類的特殊化。
純虛擬函式(pure virtual),C++中包含純虛擬函式的類,被稱為是“抽象類”。抽象類不能使用new出物件,只有實現了這個純虛擬函式的子類才能new出物件。C++中的純虛擬函式更像是“只提供申明,沒有實現”,是對子類的約束,是“介面繼承”。C++中的純虛擬函式也是一種“執行時多型”。
class A
{
public:
virtual void out1(string s)=0;//純虛擬函式
virtual void out2(string s)
{
cout<<"A(out2):"<<s<<endl;
}
};
#include <iostream>
using namespace std;
class A
{
public:
virtual void out1()=0; ///由子類實現
virtual ~A(){};
virtual void out2() ///預設實現
{
cout<<"A(out2)"<<endl;
}
void out3() ///強制實現
{
cout<<"A(out3)"<<endl;
}
};
class B:public A
{
public:
virtual ~B(){};
void out1()
{
cout<<"B(out1)"<<endl;
}
void out2()
{
cout<<"B(out2)"<<endl;
}
void out3()
{
cout<<"B(out3)"<<endl;
}
};
int main()
{
A *ab=new B;
ab->out1();
ab->out2();
ab->out3();
cout<<"************************"<<endl;
B *bb=new B;
bb->out1();
bb->out2();
bb->out3();
delete ab;
delete bb;
return 0;
}
2 . java的反射機制淺談
JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法;這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為java語言的反射機制。
java.lang.Class
Java程式在執行時,Java執行時系統一直對所有的物件進行所謂的執行時型別標識。這項資訊紀錄了每個物件所屬的類。虛擬機器通常使用執行時型別資訊選準正確方法去執行,用來儲存這些型別資訊的類是Class類。
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Java Reflection Cookbook
*
* @author Michael Lee
* @since 2006-8-23
* @version 0.1a
*/
public class Reflection {
/**
* 得到某個物件的公共屬性
*
* @param owner, fieldName
* @return 該屬性物件
* @throws Exception
*
*/
public Object getProperty(Object owner, String fieldName) throws Exception {
Class ownerClass = owner.getClass();
Field field = ownerClass.getField(fieldName);
Object property = field.get(owner);
return property;
}
/**
* 得到某類的靜態公共屬性
*
* @param className 類名
* @param fieldName 屬性名
* @return 該屬性物件
* @throws Exception
*/
public Object getStaticProperty(String className, String fieldName)
throws Exception {
Class ownerClass = Class.forName(className);
Field field = ownerClass.getField(fieldName);
Object property = field.get(ownerClass);
return property;
}
/**
* 執行某物件方法
*
* @param owner
* 物件
* @param methodName
* 方法名
* @param args
* 引數
* @return 方法返回值
* @throws Exception
*/
public Object invokeMethod(Object owner, String methodName, Object[] args)
throws Exception {
Class ownerClass = owner.getClass();
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Method method = ownerClass.getMethod(methodName, argsClass);
return method.invoke(owner, args);
}
/**
* 執行某類的靜態方法
*
* @param className
* 類名
* @param methodName
* 方法名
* @param args
* 引數陣列
* @return 執行方法返回的結果
* @throws Exception
*/
public Object invokeStaticMethod(String className, String methodName,
Object[] args) throws Exception {
Class ownerClass = Class.forName(className);
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Method method = ownerClass.getMethod(methodName, argsClass);
return method.invoke(null, args);
}
/**
* 新建例項
*
* @param className
* 類名
* @param args
* 建構函式的引數
* @return 新建的例項
* @throws Exception
*/
public Object newInstance(String className, Object[] args) throws Exception {
Class newoneClass = Class.forName(className);
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Constructor cons = newoneClass.getConstructor(argsClass);
return cons.newInstance(args);
}
/**
* 是不是某個類的例項
* @param obj 例項
* @param cls 類
* @return 如果 obj 是此類的例項,則返回 true
*/
public boolean isInstance(Object obj, Class cls) {
return cls.isInstance(obj);
}
/**
* 得到陣列中的某個元素
* @param array 陣列
* @param index 索引
* @return 返回指定陣列物件中索引元件的值
*/
public Object getByArray(Object array, int index) {
return Array.get(array,index);
}
}
3 . 執行緒同步
public class TraditionalThreadSynchronized {
public static void main(String[] args) {
final Outputter output = new Outputter();
new Thread() {
public void run() {
output.output("zhangsan");
}
}.start();
new Thread() {
public void run() {
output.output("lisi");
}
}.start();
}
}
class Outputter {
public void output(String name) {
// TODO 為了保證對name的輸出不是一個原子操作,這裡逐個輸出name的每個字元
for(int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
// Thread.sleep(10);
}
}
}
( 1). 使用synchronized將需要互斥的程式碼包含起來,並上一把鎖。
{
synchronized (this) {
for(int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
}
}
這把鎖必須是需要互斥的多個執行緒間的共享物件,像下面的程式碼是沒有意義的。
{
Object lock = new Object();
synchronized (lock) {
for(int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
}
}
每次進入output方法都會建立一個新的lock,這個鎖顯然每個執行緒都會建立,沒有意義。
(2) . 將synchronized加在需要互斥的方法上。
public synchronized void output(String name) {
// TODO 執行緒輸出方法
for(int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
}
這種方式就相當於用this鎖住整個方法內的程式碼塊,如果用synchronized加在靜態方法上,就相當於用××××.class鎖住整個方法內的程式碼塊。
(3) . volatile
volatile是第二種Java多執行緒同步的機制,根據JLS(Java LanguageSpecifications)的說法,一個變數可以被volatile修飾,在這種情況下記憶體模型(主記憶體和執行緒工作記憶體)確保所有執行緒可以看到一致的變數值,來看一段程式碼:
class Test {
static int i = 0, j = 0;
static void one() {
i++;
j++;
}
static void two() {
System.out.println("i=" + i + " j=" + j);
}
}
一些執行緒執行one方法,另一些執行緒執行two方法,two方法有可能列印出j比i大的值,按照之前分析的執行緒執行過程分析一下:
1. 將變數i從主記憶體拷貝到工作記憶體;
2. 改變i的值;
3. 重新整理主記憶體資料;
4. 將變數j從主記憶體拷貝到工作記憶體;
5. 改變j的值;
6. 重新整理主記憶體資料;
這個時候執行two方法的執行緒先讀取了主存i原來的值又讀取了j改變後的值,這就導致了程式的輸出不是我們預期的結果,要阻止這種不合理的行為的一種方式是在one方法和two方法前面加上synchronized修飾符:
class Test {
static int i = 0, j = 0;
static synchronized void one() {
i++;
j++;
}
static synchronized void two() {
System.out.println("i=" + i + " j=" + j);
}
}
根據前面的分析,我們可以知道,這時one方法和two方法再也不會併發的執行了,i和j的值在主記憶體中會一直保持一致,並且two方法輸出的也是一致的。另一種同步的機制是在共享變數之前加上volatile:
class Test {
static volatile int i = 0, j = 0;
static void one() {
i++;
j++;
}
static void two() {
System.out.println("i=" + i + " j=" + j);
}
}
one方法和two方法還會併發的去執行,但是加上volatile可以將共享變數i和j的改變直接響應到主記憶體中,這樣保證了主記憶體中i和j的值一致性,然而在執行two方法時,在two方法獲取到i的值和獲取到j的值中間的這段時間,one方法也許被執行了好多次,導致j的值會大於i的值。所以volatile可以保證記憶體可見性,不能保證併發有序性。
4 .抽象類和介面的區別
1.語法層面上的區別
1)抽象類可以提供成員方法的實現細節,而介面中只能存在public abstract 方法;
2)抽象類中的成員變數可以是各種型別的,而介面中的成員變數只能是public static final型別的;
3)介面中不能含有靜態程式碼塊以及靜態方法,而抽象類可以有靜態程式碼塊和靜態方法;
4)一個類只能繼承一個抽象類,而一個類卻可以實現多個介面。
2.設計層面上的區別
(1)抽象類是對一種事物的抽象,即對類抽象,而介面是對行為的抽象。抽象類是對整個類整體進行抽象,包括屬性、行為,但是介面卻是對類區域性(行為)進行抽象。舉個簡單的例子,飛機和鳥是不同類的事物,但是它們都有一個共性,就是都會飛。那麼在設計的時候,可以將飛機設計為一個類Airplane,將鳥設計為一個類Bird,但是不能將 飛行 這個特性也設計為類,因此它只是一個行為特性,並不是對一類事物的抽象描述。此時可以將 飛行 設計為一個介面Fly,包含方法fly( ),然後Airplane和Bird分別根據自己的需要實現Fly這個介面。然後至於有不同種類的飛機,比如戰鬥機、民用飛機等直接繼承Airplane即可,對於鳥也是類似的,不同種類的鳥直接繼承Bird類即可。從這裡可以看出,繼承是一個 "是不是"的關係,而 介面 實現則是 "有沒有"的關係。如果一個類繼承了某個抽象類,則子類必定是抽象類的種類,而介面實現則是有沒有、具備不具備的關係,比如鳥是否能飛(或者是否具備飛行這個特點),能飛行則可以實現這個介面,不能飛行就不實現這個介面。
(2)設計層面不同,抽象類作為很多子類的父類,它是一種模板式設計。而介面是一種行為規範,它是一種輻射式設計。什麼是模板式設計?最簡單例子,大家都用過ppt裡面的模板,如果用模板A設計了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它們的公共部分需要改動,則只需要改動模板A就可以了,不需要重新對ppt B和ppt C進行改動。而輻射式設計,比如某個電梯都裝了某種報警器,一旦要更新報警器,就必須全部更新。也就是說對於抽象類,如果需要新增新的方法,可以直接在抽象類中新增具體的實現,子類可以不進行變更;而對於介面則不行,如果介面進行了變更,則所有實現這個介面的類都必須進行相應的改動。
5 .Spring IOC
IOC容器的概念
IOC容器就是具有依賴注入功能的容器,IOC容器負責例項化、定位、配置應用程式中的物件及建立這些物件間的依賴。應用程式無需直接在程式碼中new相關的物件,應用程式由IOC容器進行組裝。在Spring中BeanFactory是IOC容器的實際代表者。
Spring IOC容器如何知道哪些是它管理的物件呢?這就需要配置檔案,Spring IOC容器通過讀取配置檔案中的配置後設資料,通過後設資料對應用中的各個物件進行例項化及裝配。一般使用基於xml配置檔案進行配置後設資料,而且Spring與配置檔案完全解耦的,可以使用其他任何可能的方式進行配置後設資料,比如註解、基於java檔案的、基於屬性檔案的配置都可以。
那Spring IOC容器管理的物件叫什麼呢?
Bean的概念
由IOC容器管理的那些組成你應用程式的物件我們就叫它Bean, Bean就是由Spring容器初始化、裝配及管理的物件,除此之外,bean就與應用程式中的其他物件沒有什麼區別了。那IOC怎樣確定如何例項化Bean、管理Bean之間的依賴關係以及管理Bean呢?這就需要配置後設資料,在Spring中由BeanDefinition代表,後邊會詳細介紹,配置後設資料指定如何例項化Bean、如何組裝Bean等。概念知道的差不多了,讓我們來做個簡單的例子。
package com.ljq.test;
public interface HelloService {
public void sayHello();
}
package com.ljq.test;
public class HelloServiceImpl implements HelloService{
public void sayHello(){
System.out.println("Hello World!");
}
}
public class Excute{
public HelloService helloService;
void setHelloService(HelloService helloService){//set依賴註解,也可以使用構造依賴註解
this.helloService=helloService;
}
void sayHello()
{
helloService.sayHello();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- id 表示元件的名字,class表示元件類 -->
<bean id="helloService" class="com.ljq.test.HelloServiceImpl" />
<bean id="excute"
class="com.ljq.test.Excute">
<property id="helloService">
<ref bean="helloService" />
</property>
</bean>
</beans>
package com.ljq.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 測試
*
* @author 林計欽
* @version 1.0 2013-11-4 下午10:56:04
*/
public class HelloServiceTest {
@Test
public void testHelloWorld() {
// 1、讀取配置檔案例項化一個IOC容器
ApplicationContext context = new ClassPathXmlApplicationContext("helloworld.xml");
// 2、從容器中獲取Bean,注意此處完全“面向介面程式設計,而不是面向實現”
Excute excute= context.getBean("excute", Excute.class);
// 3、執行業務邏輯
excute.sayHello();
}
}
在上面的程式碼例子中就包括控制反轉和set依賴注入。
相關文章
- Go語言最新面試題及其解析Go面試題
- Go語言面試題分享:選擇題10道(3)go語言學習Go面試題
- Go語言面試題分享:選擇題11道(2)go語言開發Go面試題
- Go語言面試題分享:選擇題10道(1)go語言學習Go面試題
- Go語言最新面試題及其解析(一)Go面試題
- 關於C語言的面試問題C語言面試
- 幾種常用程式語言的區別
- 11 種程式語言的常用按鍵
- 一家外企的面試題目(C/C++面試題,C語言面試題)面試題C++C語言
- 程式設計師晚上最常用的程式語言程式設計師
- go語言面試選擇題11道(4)go語言深入學習Go面試
- 面試挖坑題:之C語言底層操作問題面試C語言
- Go 語言精編面試50題,請收藏!Go面試
- c語言程式設計題C語言程式設計
- 前端的你常用的程式語言有哪些?前端
- 從一道坑人的面試題說函數語言程式設計面試題函數程式設計
- Go語言切片面試真題7連問Go面試
- 12個有趣的C語言面試題及答案C語言面試題
- 網站開發的常用程式語言有哪些?網站
- 函數語言程式設計中的常用技巧函數程式設計
- Go語言開發面試題分享:(判斷題13道)(6)Go面試題
- C語言常用函式C語言函式
- 乾貨來臨:C語言面試54題附答案C語言面試
- 16道嵌入式C語言面試題(經典)面試題
- 把程式語言看做語言
- iPhone 常用面試題目iPhone面試題
- Python 常用面試題Python面試題
- 面試常用英語(中英對照)面試
- 2019年c語言經典面試題目C語言面試題
- 聊聊C語言/C++—程式和程式語言C語言C++
- 最‘乾淨’的程式語言——空白程式語言
- Jquery常用面試題(總結)jQuery面試題
- C語言程式設計題T1C語言程式設計
- 【面試官問】你懂函數語言程式設計嗎?面試函數程式設計
- 征服 JavaScript 面試:什麼是函數語言程式設計?JavaScript面試函數程式設計
- ARM組合語言最常用指令組合語言
- Go語言常用的運算子篇Go
- PLSQL程式語言SQL