您的位置:首页 > 职场人生

Java学习笔记-《Java程序员面试宝典》-第四章基础知识-4.1基本概念(4.1.2)

2017-05-25 10:10 816 查看

4.1.2 Java与C/C++有什么异同

同:

都是面向对象语言,都使用了面向对象思想,都有很好的可重用性。

异:

1>语言类别

Java为解释性语言。运行过程为:编译器将java代码编译成字节码,然后由JVM解释执行;
C/C++为编译型语言,源代码经过编译链接以后生成可执行二进制代码。
因此Java执行速度比C/C++要慢,但是Java可以跨平台运行,C/C++不行。

2>全局变量

Java为纯面向对象语言,所有代码(包括函数和变量)必须在类中实现,除基本数据类型外,所有类型都是类。
Java不存在全局变量或全局函数,C++可以定义全局变量和全局函数。

3>没有指针

Java中的垃圾回收机制使得java不需要显示的分配内存,所以不存在指针的概念,程序更加安全。

4>多重继承的功能代替-接口

Java不支持多重继承,但是可以同时实现多个接口,由于接口有多态特性,因此可以用实现多个接口达到C++中多重继承的功能。

面向对象的三大特性:封装、继承、多态。

封装

定义:把对象的属性和操作(或服务)结合为一个整体,并尽可能的隐藏对象的内部实现细节。对数据的访问只能通过已定义的接口。
优点:1.可以彻底隐藏方法的内部实现,只提供一个调用的方法给其他人,其他使用这个类的人不需要知道是如何实现的,只要知道如何调用就可以了,便于软件开发中的分工合作;2.隐藏方法的内部实现,可以让保留调用方法不变的同时,随意修改类的内部结构,而不影响其他人的运行结果;3.封装可以分开类的属性,私有属性只供类自身调用,公共属性仅提供一个外部调用的方法;4.良好的封装可以减少耦合。
访问权限的控制:
public:在本项目中的任何地方都可访问
private:只能在被定义的类中使用
protected:只能被继承的类或在同一包下的其他类访问

继承--参考网站(http://www.cnblogs.com/qifengshi/p/5664983.html)

定义:基于一个已有类的定义为基础,构建新类,已有的类称为父类,新建的类称为子类。子类可以调用父类的非private修饰的成员,同时可以添加自己的新成员,设置重写父类的已有方法。
写法:一个类后面紧跟extends关键字,再加一个类的名字就表示继承于这个类。
Java允许子类定义和父类方法签名、参数、返回类型都完全一样的方法。

我们列举了一个类,代码如下:

public class Student {
private String name;
private String age;

private String handleName(String name){
return "I'm " + name;
}

private String handleAge(String age) {
return age + " 岁";
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = handleName(name);
}

public String getAge() {
return age;
}

public void setAge(String age) {
this.age = handleAge(age);
}
}

现在我们新建一个类去继承自他,这个类我们就叫Boys,其代码如下:

public class Boys extends Student {
@Override
public void setName(String name) {
super.setName(name);
}

@Override
public String getAge() {
return super.getAge();
}

@Override
public String getName() {
return super.getName();
}

@Override
public void setAge(String age) {
super.setAge(age);
}
}

在子类里面,有super关键字,表示调用方法的对象是父类的的对象,表明当子类调用这些方法时,执行的父类的方法。
注解@override表明是覆盖了父类的方法,而不是方法重载。

方法重载

定义:定义一种方法,这种方法可以承接很多类型的参数,但是只需要定义这一个方法就可以了。

下面这个例子,我们看一下什么是重载

public class OverLoading {
public void print(String args1) {
System.out.println(args1);
}

public void print(String args1, String args2) {
System.out.println(args1 + args2);
}

public void print(int args1) {
System.out.println(args1);
}

public String print(double args1) {
System.out.println(args1);
return null;
}

//这不是方法重载,这是错误的语句
//    public String print(String args1) {
//        System.out.println(args1);
//        return null;
//    }

public static void main(String[] args) {
OverLoading ol = new OverLoading();
ol.print(1.0);
ol.print(1);
ol.print("1");
ol.print("1", "1");
}

}

//输出结果
1.0
1
1
11

如上代码所示,main函数中虽然只有一个print方法,但是它却接受了多种参数,返回类型也不一样。运行的时候,系统会根据参数的不同,自动执行不同的print,这就是方法重载。
注释掉的第五个print方法,参数列表和第一个方法时重复的,所以不是方法重载,编译器会告诉你这个方法已经被定义了。
甚至更极端的情况,参数的顺序都不一样,也算是方法重载,但在这个例子里,是不存在顺序不一样的,但下面这段代码算方法重载

public String print(int args1, String args2) {
return null;
}

public String print(String agrs2, int args1) {
return null;
}

如果子类定义了一个和父类方法签名一样,但是参数列表不一样,这就算重载父类的方法,但是继承更多的是重写父类的方法,即子类和父类定义方法签名、参数列表完全一致,但实现功能不一样的方法。所以要在子类覆写方法加上@override,这样一旦写成了方法重载就会报错。

向上转型

我们使用继承,主要是因为父类和子类之间存在一个种所属关系,子类确实是父类的一种。比如我们可以把动物当做是父类,子类是猫,狗啊等等。猫,狗确实是动物的一种。动物所拥有的方法,猫,狗都有,所以猫,狗是一种类型的动物,既然如此,我们就可以把猫,狗向上转型成动物类型。这是安全且一定成功的。我们可以看下面的例子。

public class Animal {
public void run(Animal animal) {
System.out.println("动物在奔跑");
}
}
public class Dog extends Animal{
public static void main(String[] args) {
Animal animal = new Animal();
animal.run(new Dog());
}
}

这里Animal类的run方法明确规定传入的参数是Animal类型,但我们传入Dog类型也是可以成功的。这种就是向上转型的一种应用。这种转型总是成功的原因,就是上面所说,Dog其实是比Animal更专业,更独特的类型,可以看做是一个专业的类型向通用的类型转换,由一个更大的类向更小的类转换,这种转换除了会丢失一些方法和属性以外,总会是成功的。

通过向上转型将一个子类转型成父类,那么我们就可以写出下面的代码

Animal animal = new Dog();


多态--参考网址http://www.cnblogs.com/qifengshi/p/5667316.html

定义:同一个行为具有多种表现形式。同一个方法根据调用对象的不同而产生多种结果。多态就是 在程序中定义的引用变量和调用方法的代码在编译的时候就决定好了,但是引用变量指向的对象确实在运行的过程中在可以确定。
实现多态要进行如下准备:1.继承 2.方法覆写 3.向上转型。对于Java,则是通过动态绑定来实现。
动态绑定是值在运行时根据对象的类型进行绑定。
静态绑定是在编译期就进行绑定,在Java中只有final static private和构造方法是静态绑定

如果Dog覆写了Animal中的方法,那么调用这个被覆写的方法时根据上面的动态绑定的规则,我们就知道实际调用的对象是Dog,那么执行的也会是Dog类的那个方法。具体的例子如下:

public class Animal {
public void run() {
System.out.println("动物在奔跑");
}
}

public class Cat extends Animal {
@Override
public void run() {
System.out.println("猫在奔跑");
}
}

public class Dog extends Animal{
@Override
public void run() {
System.out.println("狗在奔跑");
}

public static void main(String[] args) {
Animal[] animal = {new Dog(),new Cat()};
animal[0].run();
animal[1].run();
}
}
//运行的结果
狗在奔跑
猫在奔跑

在这个例子里面,我们可以看到多态的应用,在父类中定义一个通用的run方法,在子类中去实现不同的run方法,然后将子类的对象赋给父类的引用,就可以根据子类的不同 去调用不同的run方法了。这里面,多态的实现满足了上面的三点,首先是继承关系,其次,在子类中覆写父类的方法,最后,对子类的对象进行向上转型,产生多态。

多态的好处:在添加新的动物类的时候,我们只需要在新的类里面重写,然后在调用的时候只需要new一个新的动物类就可以了。

我们在上个例子基础上在添加一段代码:

public class Log {
public void print(Animal animal) {
System.out.println(new Date().toString());
animal.run();
}

}
//我们将Dog类,修改成下面这个样子
public class Dog extends Animal{
@Override
public void run() {

System.out.println("狗在奔跑");
}

public static void main(String[] args) {
Animal[] animal = {new Dog(),new Cat()};
for (Animal item : animal) {
new Log().print(item);
}
}
}
运行的结果如下:
Wed Jul 13 17:07:40 CST 2016
狗在奔跑
Wed Jul 13 17:07:40 CST 2016
猫在奔跑

如果不采用多态,就需要在Log中根据不同的对象生成不同的print方法,使用多态以后,我们只需要在方法中编写父类的调用方法代码,然后运行时传入子类对象,就会动态绑定到子类,然后调用子类的方法。在添加新的动物类的时候,也就不用修改已有的代码了。
多态成功的区分开了“改变的事物和未改变的事物”。

一个关于深入理解多态的小题目:(参考网址http://www.cnblogs.com/jack204/archive/2012/10/29/2745150.html)

(一)相关类

class A ...{
public String show(D obj)...{
return ("A and D");
}
public String show(A obj)...{
return ("A and A");
}
}
class B extends A...{
public String show(B obj)...{
return ("B and B");
}
public String show(A obj)...{
return ("B and A");
}
}
class C extends B...{}
class D extends B...{}


(二)问题:以下输出结果是什么?

A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println(a1.show(b));   ①
System.out.println(a1.show(c));   ②
System.out.println(a1.show(d));   ③
System.out.println(a2.show(b));   ④
System.out.println(a2.show(c));   ⑤
System.out.println(a2.show(d));   ⑥
System.out.println(b.show(b));    ⑦
System.out.println(b.show(c));    ⑧
System.out.println(b.show(d));    ⑨


(三)答案

①   A and A
②   A and A
③   A and D
④   B and A
⑤   B and A
⑥   A and D
⑦   B and B
⑧   B and B
⑨   A and D

解题思路:

以第四个为例,A a2 = new B();栈中的引用变量是A, 堆中的实例变量是B,将子类实例赋值给父类的引用就是我们前面说过的向上转型。向上转型在运行时,会遗忘子类对象中与父类不同的方法,也会重写父类中的方法。a2可以调用的就是A中有但B中没有的方法,和B中重写A的方法。所以,第四条测试语句,a2.show(b)其实就是调用了B类重写了A类中的show(A object),也就是B类中的show(A object),所以结果为B and
A。
第九个,因为B继承于A,所以B可以调用A中的show(D object),所以结果为A and D。

Java中堆和栈的区别--参考网址http://blog.csdn.net/huozhicheng/article/details/6575960

函数中的基本类型的变量和对象的引用变量都在函数的栈中分配,当一段代码定义一个变量时,java就在栈中为变量分配地址,当超出这个变量的作用域后,java自动释放掉分配的空间,留作他用。
由new创建的对象和数组存放在堆中,在堆中分配的内存由垃圾回收器来管理。
引用变量就相当于为数组或对象起的一个名称,以后就可以用栈中的引用变量去访问堆中的数组或对象。
Java中变量在内存中的分配:
类变量(static修饰的变量):在程序加载时就在堆中开辟了内存,堆中的内存地址存放在栈中便于高速访问。静态变量的生命周期一直持续到整个“系统”关闭。
实例变量:使用new关键字时,在堆中开辟不一定是连续的空间给变量,然后根据哈希算法转换为一长串数字表征这个变量在堆中的“物理位置”。当实例变量的引用丢失以后,将被GC(garbage collector)列入回收的名单,并不会被马上回收,达到某一条件(比如内存占用过高)才会回收。
局部变量:执行到它时才会给他在栈中开辟内存,当局部变量一旦脱离作用域,内存马上释放。

接口

具体参见http://blog.csdn.net/nvd11/article/details/41129935,个人认为讲的很好,很全面。

5>内存分配。

C++需要程序员手动去分配内存,Java语言提供垃圾回收机制不需要显示分配内存。

6>平台无关性

Java由于有平台无关性,每种数据类型的长度都是一定的,如int就是4个字节。而c/c++同一数据类型在不同的平台会分配不同的字节数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 程序员 语言 面试
相关文章推荐