前言:
面试常(送)考(分)题:java三大特性是啥并展开解释下。说是送分其实也并没1+1那么简单,很多人会写代码但说不上来,其实也是没完全理解java的表现。
理解了三大特性其实也就理解了java。
一、封装【能否直接调用】
思想:
将具体的操作细节打包,用户只需看见需要看到的内容即可。
体现:
类中的成员变量用private修饰,类内可以直接调用,类外不能直接访问,只能通过该类中getter/setter等public的方法调用。
四种权限修饰符的作用范围
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | yes | |||
(缺省) | yes | yes | ||
protected | yes | yes | yes | |
public | yes | yes | yes | yes |
关键字this
指代:
this指的是:谁调用指谁
super指的是父类。
用法:
this理解为:当前对象或当前正在创建的对象【在构造器中使用就指的是正在创建的对象】
【不可出现在static方法中:static不用通过实例访问,故this没的指代】
this可以修饰:成员变量、成员方法、构造器。以下为this的5种用法:
- 在类的方法中,用this调用当前对象的成员变量和方法:
e.g.一般情况下this可省略,除非:此处参数的名称从a改为age->第三条
public void setAge(int a) {
this.age = a;
}
- 在类的构造器中,用this调用当前正在创建的对象的的成员变量和方法:
e.g.一般情况下this可省略,除非:此处参数的名称从n改为name->第三条
public Person(String n) {
this.name = n;
}
- 区分同名成员变量和局部变量
e.g.
class Person{
private String name;
private int age;
public Person(String name) {
this.name = name;
}
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;
}
}
其中构造器和两个setter方法中的初始化赋值语句都用了this.来指代当前对象的属性(成员变量),来区别于成员方法参数中的同名局部变量。【这种情况下,this不可省略!】
如果不同名,this可以不写,因为此时系统分得清哪个是局部变量哪个是成员变量,如下:
但如果同名了也不用this,系统无法辨别谁是谁,于是就会就近地认为两个name都指的是传进来的参数(局部变量)。此时并不会出现红线报错,因为赋值给自己是合法的,但在实际运行时会出现并没将参数传进成员变量的结果。
-
调用构造器:
在构造器中调用其他构造器:显示的用this(形参列表)来指定调用哪个构造器。
作用:同样的代码封装在一个构造器里,其他的要用就直接this调用,降低代码冗余程度。
规定:this(形参列表)这一行必须在该构造器的首行。所以一个构造器最多调用其它构造器一次,两个的话就必有一行this()在非首行。
注意:自己不能调用自己,也不能闭环调用:a调b,b调a ->死循环。所以一个类中假设有n个构造器,则最多有n-1个构造器中使用this调用其他构造器。 -
引用当前对象:
e.g. return this;
也可以作为参数:boy.marry(this);
二、继承【能否获取公共部分】
思想:
多个类的公共部分可以提取出来形成一个父类,这样一来可以减少代码的冗余,提高代码的复用性、可扩展性。
体现:
class A extends B{} 其中A是子类,B是父类。那A就获取了B中声明的所有的属性和方法。A可以定义自己特有的属性和方法,实现功能的扩展 - > 说明子类功能更强大一点~
注意:
当父类有private修饰的属性时,子类继承到了该属性,但是受封装性【诶这不就和第一大特性连起来了吗!-> 特性各司其职】影响不可直接调用。
规定:
- java仅支持单继承,但是可以实现多个接口
- 父、子类是相对的概念,java支持多层继承关系。
- 若没有显示声明父类,则该类默认继承与java.lang.Object类。也即所有java类都直接或间接地继承java.lang.Object类。
重写
概念:子类继承父类之后可以对父类的方法进行覆盖操作,以实现子类特色的功能。
体现:子类中有一个和父类方法同名同参数列表的方法,唯一不同就是方法体不同。
注意:
- 父类中用private、static修饰的方法不可被重写
- 子类重写方法的权限修饰符大于等于父类中被重写方法的权限修饰符
- 若父类被重写方法返回值类型是void,则子类重写方法只能是void
- 若父类被重写方法返回值类型是A类(引用数据类型),则子类重写方法可以是A类或A类的子类
- 若父类被重写方法返回值类型是基本数据类型,则子类重写方法只能是相同的基本数据类型
- 子类重写方法抛出的异常类型小于等于父类被重写方法抛出的异常类型
重载vs重写: 两者就没啥关系,就是名字有点像而已。
重载:2同1不同–同一个类中,方法名相同;参数列表中或是个数或是类型不同;与权限修饰符和返回值类型无关。
重写:出现在继承关系的子类中,子类和父类方法同名同参数列表,但方法体不同。
关键字super
super的3种方法:
1.调用父类成员变量
2.调用父类成员方法
3.调用父类构造方法
*注意:
子类的构造函数都会默认调用父类构造方法super(),除非以下两种情况:
1)子类构造方法中显示调用了指定的父类的某带参构造方法。【super调用构造方法都应写在子类构造方法中的第一行。】
2)该构造方法第一行用this调用子类的另一个构造方法。【因为里面包含了super,不能在一个子类构造方法中同时存在两个super调用父类的构造方法。】
实例
Father.java 父类
package Extends;
public class Father {
public String name;
protected int age;
boolean state; //默认访问权限
private int id;
/*test作为测试成员变量*/
public String test = "我是爸爸";
public String getTest() {
return test;
}
public Father() {
System.out.println("Father-无参构造函数");
}
public Father(String name, int age, boolean state, int id) {
this.name = name;
this.age = age;
this.state = state;
this.id = id;
System.out.println("Father-带参构造函数");
}
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 int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public boolean isState() {
return state;
}
public void setState(boolean state) {
this.state = state;
}
@Override
public String toString() {
return "Father{" +
"name='" + name + '\'' +
", age=" + age +
", state=" + state +
", id=" + id +
'}';
}
}
Son.java 子类
package Extends;
public class Son extends Father{
public String habits;
/*test作为测试成员变量*/
public String test = "我是儿子";
public String getTest() {
return test;
}
public Son() {
//super(); //子类构造函数默认调用父类的无参构造函数 默认省略子类构造函数第一行super()
System.out.println("Son-无参构造函数");
}
public Son(String habits) {
//super(); //子类构造函数默认调用父类的无参构造函数 默认省略子类构造函数第一行super()
this.habits = habits;
System.out.println("Son-带参构造函数1");
}
public Son(String name, int age, boolean state, int id, String habits) {
super(name, age, state, id); //子类构造函数可以显示调用父类的带参构造函数,此行不可省略。显式调用后在此构造函数中系统不会再调用父类无参构造函数。
this.habits = habits;
System.out.println("Son-带参构造函数2");
}
public String getHabits() {
return habits;
}
public void setHabits(String habits) {
this.habits = habits;
}
@Override
public String toString() {
return "Son{" +
"habits='" + habits + '\'' +
", name='" + name + '\'' +
", age=" + age +
", state=" + state +
'}';
}
}
ExtendsTest.java 测试类
package Extends;
public class ExtendsTest {
public static void main(String[] args) {
Father f1 = new Father(); //Father-无参构造函数
f1.setName("nibaba");
f1.age = 21;
System.out.println(f1.toString()); //Father{name='nibaba', age=21, state=false, id=0}
Father f2 = new Father("NiBABA",22,true,310110199); //Father-带参构造函数
System.out.println(f2.toString()); //Father{name='NiBABA', age=22, state=true, id=310110199}
Son s = new Son(); //Father-无参构造函数 Son-无参构造函数
s.setHabits("coding");
s.setAge(10);
s.setName("SON");
s.setId(410110122);
System.out.println(s.toString()); //Son{habits='coding', name='SON', age=10, state=false}
Son s1 = new Son("singing"); //Father-无参构造函数 Son-带参构造函数1
s1.name ="son1";
s1.age = 11;
s1.habits = "dancing";
System.out.println(s1.toString()); //Son{habits='dancing', name='son1', age=11, state=false}
Son s2 = new Son("son2",12,true,310110111,"hahaha"); //Father-带参构造函数 Son-带参构造函数2
System.out.println(s2.toString()); //Son{habits='hahaha', name='son2', age=12, state=true}
/*测试:test*/
Father test = new Son();
System.out.println(test.test); //我是爸爸
System.out.println(test.getTest()); //我是儿子
}
}