访问权限的修饰符
访问权限的修饰符一共有四种:private,默认,protected,public
private | 默认 | protected | public | |
---|---|---|---|---|
类(非内部类) | N | Y | N | Y |
方法 | Y | Y | Y | Y |
属性 | Y | Y | Y | Y |
权限修饰符在类上:
如果类有public 是可以随便访问的(包括跨文档);
如果 默认 的话,同包内能访问,跨包不能访问。
权限修饰符在属性或者方法上:
作用域 | 同一个类 | 同包 | 子孙类 | 无关类 |
---|---|---|---|---|
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
默认 | Y | Y | N | N |
private | Y | N | N | N |
通常情况下:类一般设置成public的,属性一般都是private,方法一般都是public的,也有少数使用protected,private。
继承
描述类的时候,如果发现几个类有重复的属性和方法,就可以采用继承的方式来继承。
继承语法:使用extends关键字来继承
class 子类 extends 父类{
子类属性
子类方法
}
1、使用 extends 指定父类;
2、Java 中一个子类只能继承一个父类 (而C++/Python等语言支持多继承);
3、子类会继承父类的所有 public 的字段和方法;
4、对于父类的 private 的字段和方法,子类中是无法访问的;
5、子类的实例中,也包含着父类的实例,可以使用 super 关键字得到父类实例的引用;
继承的特点:
1、子类使用extends 关键字继承父类的共同属性以外,子类还可以有自己特有的属性或者方法;
2、父类更通用,子类更具体;
3、子类只能获得父类中的非private的属性,如果想要继承就得提供公共的 set和get方法,私有的方法是无法继承下来的;
4、java中只能做单继承(一个子类只能继承一个父类 );
在类的继承结构中,如果创建一个子类的对象,那么会在子类的对象的内部常见一个父类的对象,
如果通过子类的对象访问属性的时候,子类中没有该属性,就会到父类的对象中查找,
调用方法的时候也是一样的,如果子类中没有该方法,就会到父类中查找。
protected 关键字
对于类的调用者来说,protected 修饰的字段和方法是不能访问的;
对于类的子类和同一个包的其他类来说,protected 修饰的字段和方法是可以访问的。
final 关键字
功能:限制类被继承
1、如果 final 修饰在变量上,这个变量不能修改。
2、如果 final 修饰在属性上,要么直接初始化,要么在构造器中初始化,
如果存在多个构造器,每一个构造器里面都必须要有 final 修饰的这个属性的初始化。
3、如果 final 修饰在方法上,final修饰的方法不能被覆写。
4、如果 final 修饰在类上,这个类就不能被继承。
语法:
权限修饰符 final 返回值类型 方法名(参数列表){
方法体
}
super 关键字
如果需要在子类内部调用父类方法怎么办? 使用 super 关键字。
super 表示获取到父类实例的引用
a) 使用 super 来调用父类的构造器
public Bird(String name) {
super(name);
}
b) 使用 super 来调用父类的普通方法
public class Bird extends Animal {
public Bird(String name) {
super(name);
}
@Override
public void eat(String food) {
// 修改代码, 让子调用父类的接口.
super.eat(food);
System.out.println("我是一只小鸟");
System.out.println(this.name + "正在吃" + food);
}
}
1.子类通过默认构造器实例化的过程中,父类的构造器先被调用,
然后再调用子类的构造器,在子类构造器内部默认调用 super();
class JavaTeacher extends Teacher{
boolean isGlass;
public JavaTeacher(){
super(); //隐藏调用父类的构造器
//写不写,都存在
System.out.println(...);
}
}
注意:
1、如果父类带有 有参数的构造器,并且没有显示的定义父类的默认构造器,默认构造器被覆盖,
导致子类的构造器无法通过 super() 来做调用,就会编译报错,需要显示的定义父类的默认的构造器。
2、子类通过构造器实例化时要调用父类的默认构造器,是隐含的调用了 super()这个方法。
但是如果子类的构造器中调用了父类中的有参数的构造器,那么无参数的构造器就不会被调用了。
3、super() 的调用必须要放在方法的第一行。
4、super() 可以表示父类的引用,我们可以使用super 和 this 来区分父类和子类中同名的属性。
在子类中访问属性的查找顺序首先在子类中查找指定的属性名字,
如果在子类中没有该属性,那么就去父类中去查找该属性。
方法也是一样的,当子类和父类有同样的方法时,通过子类调用的这个相同的方法时调用的是子类的方法。
但是我们可以通过super对父类的引用来调用父类的方法。
super().方法(); //super 调用父类方法
this关键字:
1、代表当前类的指定实例的引用
2、可以区分同名的属性和局部变量
3、通过this可以调用同类中的构造器(this(),this(参数列表))
4、调用本类里面的属性,this.属性名,this.方法()
如果父类、子类有重名的属性,this就是子类的属性
No | 区别 | this | super |
---|---|---|---|
1 | 概念 | 访问本类中的属性和方法 | 由子类访问父类中的属性、方法 |
2 | 查找范围 | 先查找本类,如果本类没有就调用父类 | 不查找本类而直接调用父类 |
3 | 特殊 | 表示当前对象 | 无 |
封装
提高数据安全性,通过封装,可以实现对属性的访问权限控制,同时增加了程序的可维护性。
private 实现封装:
将属性私有化,使用 private关键字 完成属性私有化,这种属性只有本类能访问。
(在其他类中,通过调用 这个类的公有方法,从而间接访问 这个类的私有属性)
如果需要获取或者修改这个 private 属性,就需要使用 getter / setter 方法。
class Girl{
private String name;
private int age;
private String tel; //封装
public void setName(String myName){ //给 name 赋值
name=myName;
}
public String getName(){ //获取 name
return name;
}
public void setAge(String myAge){
age=myAge;
}
public int getAge(){
return age;
}
//对象方法
public void playGame(){
System.out.println(this.name+"玩游戏");
System.out.println(name); //隐藏的就是 this
}
public void sleep(){
System.out.println(this.name+"睡觉");
playGame(); //this.playGame(); 省略 this,this就是调用playGame的p
//对象方法调用对象方法
}
}
public class TeatGirl{
public static void main(String[] args){
Girl g=new Girl();
g.setName("一一"); //通过公有的方法设置属性
g.setAge(18);
String myName=g.getName(); //通过公有的方法获取属性的值
int myAge=g.getAge();
g.playName(); //this 相当于 p (this.name/p.name) 即:哪个引用调 this,this 就相当于 哪个
}
}
多态
多态就是行为具有表现多种功能的能力。
继承多态,不仅仅是父类时抽象类,父类是普通类也是可以的。
多态的条件:
1、子类对父类有方法重写
2、父类的引用指向子类的实例(子类转换成父类),
调用 覆写的这个方法 的时候,调用的是子类的方法。
向上转型:
Bird bird = new Bird("圆圆");
Animal bird2 = bird;
// 或者写成下面的方式
Animal bird2 = new Bird("圆圆");
// bird2 是一个父类(Animal)的引用,指向一个子类(Bird)的实例,这种写法称为“向上转型”。
动态绑定:
当子类和父类中出现同名方法的时候, 再去调用会出现什么情况呢?
// Animal.java
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println("我是一只小动物");
System.out.println(this.name + "正在吃" + food);
}
}
// Bird.java
public class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void eat(String food) {
System.out.println("我是一只小鸟");
System.out.println(this.name + "正在吃" + food);
}
}
// Test.java
public class Test {
public static void main(String[] args) {
Animal animal1 = new Animal("圆圆");
animal1.eat("谷子");
Animal animal2 = new Bird("扁扁");
animal2.eat("谷子");
}
}
// 运行结果
我是一只小动物
圆圆正在吃谷子
我是一只小鸟
扁扁正在吃谷子
1、animal1 和 animal2 虽然都是 Animal 类型的引用,
但 animal1 指向 Animal 类型的实例,animal2 指向 Bird 类型的实例。
2、animal1 和 animal2 分别调用 eat 方法,
而 animal1.eat() 实际调用了父类的方法,animal2.eat() 实际调用了子类的方法。
在 Java 中,调用某个类的方法,究竟执行了哪段代码(是父类方法的代码还是子类方法的代码),
要看究竟这个引用指向的是父类对象还是子类对象,这个过程是程序运行时决定的(而不是编译期)。
使用多态的优点:
封装是让类的调用者不需要知道类的实现细节;
多态能让类的调用者连这个类的类型是什么都不必知道,只需要知道这个对象具有某个方法即可。