文章目录
本博客适用于新手学习过程中对java面向对象的一些问题的解答。
一、编程题:设计Src和Dis两个类,Src中有一个被封装的属性,类型为int(要求为非负值),每当通过特定方法更改Src对象中的这个属性时,Dis对象都能得到通知,并向Src发消息获得此属性值
import java.util.Scanner;
class Dis {
private Src sc;
public void setSc(Src sc) {
this.sc = sc;
}
public void show(){
System.out.println("i am dis ,Age was changed,and the new age is "+sc.getAge());
}
}
class Src {
private int age;
private Dis ds;
public Src( int age){
this.age = age;
}
public void setAge(int age) {
if(age>0) {
this.age = age;
}
else{
System.out.println("输入的数据有误");
}
}
public void setDs(Dis ds1){
this.ds = ds1;
}
public int getAge()
{
return age;
}
public void changeAge(int age1){
age= age1;
ds.show();
}
}
public class javaDemo {
public static void main(String args[]) {
Scanner sc1 = new Scanner(System.in);//1
Dis ds = new Dis();//2
Src sc = new Src(8);//3
sc.setDs(ds);//4
ds.setSc(sc);//5
int newAge = sc1.nextInt();//6
sc.changeAge(newAge);//7
}
}
在这里。两个类中的数据成员都有对方的对象引用,并且有对应的set函数,在主函数的4,5,两行,同时也正是这两个方法建立了两个对象之间的联系。每一次调用changeage方法改变Sc的age属性时,都会调用ds的show方法,使得ds得到通知,同时,二者之间的联系,使得ds可以通过调用内部的Src对象成员来获得改变后的值。
二、Java的访问控制修饰符有哪些?各有什么访问权限?
访问控制权限就是 封装
,其核心的一点就是: 只对需要的类可见。
Java
的访问控制修饰符有4种:public
,protected
,private
以及默认(即为缺省,无任何修饰符)。具体情况请看上图。
可以对类或者类中的成员(字段和方法)加上访问修饰符。
- 类可见表示其他类可以用这个类创建实例对象
- 成员可见表示其他类可以用这个类的实例对象访问到该成员。
protected
用于修饰成员,表示在继承体系中成员对于子类可见,但是这个访问修饰符对于类没有意义。
字段是绝对不能是公有的,如果这样做了,客户端可以对其随意修改,那么就失去了这个字段修改行为的控制。可以用公有的getter
和setter
方法来替换公有字段,这样的话就可以控制对字段的修改行为。
但是也有例外,如果是包级私有的类或者私有的嵌套类,那么直接暴露成员不会有特别大的影响。
public class AccessWithInnerClassExample {
private class InnerClass{
int x;
}
private InnerClass innerClass;
public AccessWithInnerClassExample(){
innerClass=new InnerClass();
}
public int getValue(){
//直接访问
return innerClass.x;
}
}
代码如下(示例):
类前修饰符为public
public class Palne {
public String name;
protected int age;
private int id;
public Palne(String name,int age,int id){
this.name = name;
this.id = id;
this.age =age;
}
public int getId() {
return id;
}
public int setId(){
return id;
}
protected void show1(){
System.out.println("It's age is"+age);
}
private void show2(){
System.out.println("It's id is"+id);
}
}
public class test1 {
public static void main(String args[]){
Palne pa = new Palne("苏35",18,4);
System.out.println(pa.name);
System.out.println(pa.age);
System.out.println(pa.id);
pa.getId();
pa.show1();
pa.show2();
}
}
报错如图所示:
修改后,结果如下:
苏35
18
It's age is18
当我们把类前修饰符换为缺省,在将测试类javaDemo放入另一个包中,效果如下图:此时,Plane类只能由包中的类进行访问,包外的类没有访问权限。
三:子类对于从父类继承的哪些属性与方法是可见的?
子类继承了父类的所有属性和方法, 但只有public、protected的属性和方法在子类是可见的。
子类在继承父类的时候,首先应该满足父类可被访问,例如当子类和父类不在同一个包当中时,父类修饰符必为public;
在父类能被访问的前提下,凡是修饰符为public或是protected的父类属性成员或是方法能被子类所访问;private的属性成员或是方法则不能被访问。
代码如下(示例):
class Plane{
protected String name;
public int id;
private int age;
public Plane(String name,int id,int age){
this.name = name;
this.id = id;
setAge(age);
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
class FighterPalne extends Plane{
private int missileNum;
public void setMissileNum(int num){
this.missileNum = num;
}
public FighterPalne(String name,int id,int age,int missileNum){
super(name,id,age);
setMissileNum(missileNum);
}
public int getMissileNum() {
return missileNum;
}
public void show(){
System.out.println("飞机信息:"+this.name+" 编号:"+this.id+" 服役年龄: "+this.getAge());
System.out.println("携带弹药数量: "+this.getMissileNum());
}
}
public class javaDemo{
public static void main(String args[]){
FighterPalne pa =new FighterPalne("苏35",6087,4,6);
pa.show();
}
}
结果如下:
飞机信息:苏35 编号:6087 服役年龄: 4
携带弹药数量: 6
子类直接调用父类的private属性会报错,即不可见。但是我们可以通过set(),get()。两个公有方法来实现子类对父类private数据的调用。
四: 什么是组合?有什么作用?
组合就是把一个类当成另一个类的组合成分,从而允许新类直接复用该类的public方法。组合是一种 has-a
的关系,可以理解为有一个。
作用:不破坏封装,整体类与局部类之间松耦合,彼此相对独立且具有更好的可扩展性。
本题的实例代码请看题一。
五: 什么是重载?有什么作用?
存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。
代码如下(示例):
public class javaDemo {
public static void main(String args[]){
int resultA = sum(10,20);
System.out.println("结果:"+resultA);
int resultB = sum(10,20,30);
System.out.println("结果:"+resultB);
int resultC = sum(10.0,20.0);
System.out.println("结果:"+resultC);
}
public static int sum(int a,int b){
return a+b;
}
public static int sum(int a,int b,int c){
return a+b+c;
}
public static int sum(double a,double b){
return (int)(a+b);
}
}
本程序中在主类中一共定义了3个sum()方法,但是这三个方法的参数个数,以及数量完全不相同,那么证明了sum()方法已经被重载了,而在调用方法时,虽然方法的调用名称相同,但是会根据其声明的参数个数或类型执行不同的方法体。
重载的注意事项:
- 返回值不同,其他都相同不算是重载。
- 代码编写过程中,重载的多个方法往往存在一定的调用关系,即一个方法写有实现功能,其他方法采用委托方式进行调用。
代码如下(示例):
class Person{
private String name;
private int age;
public Person(){
System.out.println("*** 一个新的Person类对象实例化了 ***");
}
public Person(String name){
this();
this.name = name;
}
public Person(String name,int age){
this(name);
this.age =age;
}
public void tell(){
System.out.println("姓名:"+this.name+", 年龄: "+this.age);
}
}
public class javaDemo{
public static void main(String args[]){
Person per = new Person("张三",12);
per.tell();
}
}
本程序利用this()实现了本类其他构造方法的调用,但是此语句使用时必须放在构造方法首行。
六: 什么是覆盖?有什么作用?
覆盖也叫重写,覆写。
存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法。
重写有以下3个限制:
- 子类方法的访问权限必须大于等于父类方法
- 子类方法的返回类型必须是父类方法返回类型或者为其子类型
- 子类方法抛出的异常类型必须是父类抛出异常类型或者为其值类型。
可以使用@Override
注解,让编译器帮忙检查是否满足上面的三个限制条件。
示例如下:
public class SuperClass {
protected List<Integer>func()throws Throwable{
return new ArrayList<>();
}
}
public class SubClass extends SuperClass{
@Override
public ArrayList<Integer>func()throws Exception{
return new ArrayList<>();
}
}
以上的示例中,SubClass
为SuperClass
的子类,SubClass
重写了SuperClass
的func()
方法。其中:
- 子类的访问权限为
public
,大于父类的protected
- 子类的返回类型为
ArrayList<Integer>
,是父类返回类型List<Integer>
的子类 - 子类抛出的异常类型为 Exception,是父类抛出异常 Throwable 的子类
- 子类重写方法使用 @Override 注解,从而让编译器自动检查是否满足限制条件
方法调用的优先级
在调用⼀个方法时,
先从本类中查找看是否有对应的方法,
如果没有再到父类中查看,看是否从父类继承来。
否则就要对参数进行转型,转成父类之后看是否有对应的方法。
总的来说,方法调用的优先级为:
- this.func(this)
- super.func(this)
- this.func(super)
- super.func(super)
class A {
public void show(A obj){
System.out.println("A.show(A)");
}
public void show(C obj){
System.out.println("A.show(C)");
}
public static void main(String[] args) {
A a=new A();
B b=new B();
C c=new C();
D d=new D();
//在A中存在show(A obj),直接调用
a.show(a);//A.show(A)
//在A中不存在show(B obj),将 B 转型成其父类 A
a.show(b);//A.show(A)
//在 B中存在从A 继承来的show(C obj),直接调用
b.show(c);//A.show(C)
// 在 B 中不存在 show(D obj),但是存在从 A 继承来的 show(C obj),将 D 转型成其⽗类 C
b.show(d);//A.show(c)
// 引⽤的还是 B 对象,所以 ba 和 b 的调⽤结果⼀样
A ba=new B();
ba.show(c);// A.show(C)
ba.show(d);// A.show(C)
}
}
class B extends A{
@Override
public void show(A obj){
System.out.println("B.show(A)");
}
}
class C extends B{
}
class D extends C{
}
覆盖的注意事项:
- 子类方法覆盖父类方法,子类的访问修饰符权限应等于或者大于父类。确保可以使用父类实例的地方都可以使用子类实例去代替。
- 同名的static方法和非static方法不能相互覆盖。
- 方法前有final修饰符,此方法不能在子类方法中进行覆盖
- 在JDK中,很多父类的方法被子类重新覆盖,赋予了不同的含义,如Object类中的boolean equals(Object obj)方法
- 抽象类中如果存在抽象方法,则具体子类必须对抽象方法进行覆盖
七、super关键字
- 访问父类的构造函数:可以使用
super()
函数访问父类的构造函数,从而委托父类完成一些初始化的工作,注意,子类一定会调用父类的构造函数来完成初始化工作,一般是调用父类的默认构造函数,如果子类需要调用父类其他构造函数,就可以使用super()
函数。 - 访问父类的成员:如果子类重写了父类的某个方法,可以通过使用
super
关键字来引用父类的方法实现。
public class SuperExample {
protected int x;
protected int y;
public SuperExample(int x,int y){
this.x=x;
this.y=y;
}
public void func(){
System.out.println("SuperExample.func()");
}
}
public class SuperExtendExample extends SuperExample{
private int z;
public SuperExtendExample(int x,int y,int z){
super(x,y);
this.z=z;
}
@Override
public void func(){
super.func();
System.out.println("SuperExtendExample.func()");
}
public static void main(String[] args) {
SuperExample e=new SuperExtendExample(1,2,3);
e.func();
}
}
结果如下:
SuperExample.func()
SuperExtendExample.func()