1. 封装
1.1 概念
若进行封装就需要使用访问限定符(稍后详细解释)。
1.2 封装的实现
在代码层面就是把用public 修饰的成员变量/方法改为用private 修饰
代码如下:
class Dog{
private String name ;
private int age ;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println(this.name + "正在吃饭!");
}
public static void main(String[] args) {
Dog dog = new Dog("小黄",3);
System.out.println(dog.name);
}
}
在同一个包同一个类中,private 修饰的成员变量/方法 可以被直接访问:
除此之外,其他三种情况下都不可以被直接访问:
不过,我们可以通过 get 和 set 方法对private 修饰的成员变量/方法进行间接访问:
class Dog{
private String name ;
private int age ;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
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 void eat(){
System.out.println(this.name + "正在吃饭!");
}
}
public class test1 {
public static void main(String[] args) {
Dog dog = new Dog("小黄",3);
System.out.println(dog.getName());
}
}
运行结果如下:
2. 包
2.1 概念
2.2 如何导入包中的类
1. 直接使用包名导入,例如Date类:可以使用 java.util.Date 导入 java.util 这个包中的 Date类.
不过这个使用有点麻烦。
2. 使用 import语句导入包
这个就方便多了。如果需要使用 java.util 中的其他类, 可以使用 import java.util.*
不过这样存在一些问题:容易出现冲突的情况.
这种情况下,需要使用完整的类名
所以我们更建议显式的指定要导入的类名.
2.3 自定义包
2.3.1 基本规则
- 在文件的最上方加上一个 package 语句指定该代码在哪个包中.
- 包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.dadu.www ).
- 包名要和代码路径相匹配. 例如创建 com.dadu.www 的包, 那么会存在一个对应的路径 com.dadu.www来存储代码.
- 如果一个类没有 package 语句, 则该类被放到一个默认包中
2.3.2 操作步骤(以ideal为例)
2.4 控制包访问权限举例
1.访问 public 修饰的变量
2. 访问 private 修饰的变量
报错,因为 name 是私有的,不允许其他的类访问。
3. 访问 default 修饰的变量
报错,因为default 修饰的变量不允许被其他包中的类访问。
2.5 其他的包
3.继承
3.1 概念
3.2 继承的语法
在Java中如果要表示类之间的继承关系,需要借助extends关键字,具体如下:
修饰符 class 子类 extends 父类 {// ...//...}
class Animal {
public String name;
public int age;
public void eat(){
System.out.println(name + "正在吃饭");
}
public void sleep(){
System.out.println(name + "正在睡觉");
}
}
class Dog extends Animal{
public void bark(){
System.out.println(name +"汪汪叫!");
}
}
class Cat extends Animal{
public void mew(){
System.out.println(name +"喵喵叫!");
}
}
public class test2{
public static void main(String[] args) {
Dog dog = new Dog();
// dog类中并没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的
System.out.println(dog.name);
System.out.println(dog.age);
// dog访问的eat()和sleep()方法也是从Animal中继承下来的
dog.eat();
dog.sleep();
dog.bark();
}
}
运行结果如下:
3.3 父类成员访问
3.3.1 子类对象访问成员变量
1. 子类和父类不存在同名成员变量
class Data{
public int a;
public int b;
}
class Num extends Data{
public int c;
public void func(){
a = 10; // 访问从父类中继承下来的a
b = 20; // 访问从父类中继承下来的b
c = 30; // 访问子类自己的c
System.out.println(a + " "+b + " " + c);
}
}
public class test3 {
public static void main(String[] args) {
Num n = new Num();
n.func();
}
}
class Data{
public int a = 10;
public int b = 20;
public int c = 30;
}
class Num extends Data{
public int c = 60;
public void func(){
System.out.println(a + " "+b + " " + c);
}
}
public class test3 {
public static void main(String[] args) {
Num n = new Num();
n.func();
}
}
运行结果如下:
3. 子类和父类都没有同一个变量
- 如果访问的成员变量子类中有,优先访问自己的成员变量。
- 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
- 如果访问的成员变量与父类中成员变量同名,则优先访问自己的。
3.3.2 子类对象访问成员方法
class Data{
public void func1(){
System.out.println("Data中的func1方法!");
}
}
class Num extends Data{
public void func2(){
System.out.println("Num中的func2方法!");
}
public void func3(){
func1();
func2();
}
}
public class test3 {
public static void main(String[] args) {
Num n = new Num();
n.func3();
}
}
运行结果如下:
class Data{
public void func1(){
System.out.println("Data中的func1方法!");
}
public void func2(){
System.out.println("Data中的func2方法!");
}
}
class Num extends Data{
public void func1( int a){
System.out.println("Num中的func1(int)方法!");
}
public void func2(){
System.out.println("Num中的func2方法!");
}
public void func3(){
func1(); // 没有传参,访问父类中的func1()
func1(20); // 传递int参数,访问子类中的func1(int)
func2(); // 直接访问,则永远访问到的都是子类中的func2(),基类的无法访问到
}
}
public class test3 {
public static void main(String[] args) {
Num n = new Num();
n.func3();
}
}
运行结果如下:
- 通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。
- 通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错;
3.4 super 关键字
class Data{
int a;
int b;
public void func1(){
System.out.println("Data中的func1方法!");
}
public void func2(){
System.out.println("Data中的func2方法!");
}
}
class Num extends Data{
int a; // 与父类中成员变量同名且类型相同
char b; // 与父类中成员变量同名但类型不同
// 与父类中func1()构成重载
public void func1( int a){
System.out.println("Num中的func1(int)方法!");
}
// 与父类中func2()构成重写
public void func2(){
System.out.println("Num中的func2方法!");
}
public void func3(){
// 对于同名的成员变量,直接访问时,访问的都是子类的
a = 100; // 等价于: this.a = 100;
b = 101; // 等价于: this.b = 101;
// 注意:this是当前对象的引用
// 访问父类的成员变量时,需要借助super关键字
// super是获取到子类对象中从基类继承下来的部分
super.a = 200;
super.b = 201;
// 父类和子类中构成重载的方法,直接可以通过参数列表区分清访问父类还是子类方法
func1(); // 没有传参,访问父类中的func1()
func1(20); // 传递int参数,访问子类中的func1(int)
// 如果在子类中要访问重写的基类方法,则需要借助super关键字
func2(); // 直接访问,则永远访问到的都是子类中的func2(),基类的无法访问到
super.func2(); // 访问基类的func2()
}
}
public class test3 {
public static void main(String[] args) {
Num n = new Num();
n.func3();
}
}
运行结果如下:
3.5 子类构造方法
class Data{
public Data(){
System.out.println("Data()");
}
}
class Num extends Data{
public Num(){
System.out.println("Num()");
}
}
public class test3 {
public static void main(String[] args) {
Num n = new Num();
}
}
运行结果如下:
class Data{
public int a;
public int b;
public Data(int a,int b){
this.a = a;
this.b = b;
}
}
class Num extends Data{
public int c;
public Num(int a,int b,int c){
super(a,b);
this.c = c;
}
public void func(){
System.out.println(a + " "+b + " " + c);
}
}
public class test3 {
public static void main(String[] args) {
Num n = new Num(10,20,30);
n.func();
}
}
运行结果如下:
3.6 super 和 this
3.7 继承方式
注意:
3.8 final 关键字
3.9 继承和组合
示例如下:
// 轮胎类
class Tire{
// ...
}
// 发动机类
class Engine{
// ...
}
// 车载系统类
class VehicleSystem{
// ...
}
class Car{
private Tire tire; // 可以复用轮胎中的属性和方法
private Engine engine; // 可以复用发动机中的属性和方法
private VehicleSystem vs; // 可以复用车载系统中的属性和方法
// ...
}
// 奔驰是汽车
class Benz extends Car{
// 将汽车中包含的:轮胎、发送机、车载系统全部继承下来
}
4. 访问限定符
1. 同一个包的同一类
示例如下:
2. 同一个包不同类
示例如下:
3.不同包中的子类
4. 不同包的非子类
5. 多态
5.1 概念
5.2 实现条件
class Animal {
public String name;
public int age;
public Animal(String name,int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println(name + "正在吃饭");
}
}
class Dog extends Animal{
public Dog(String name,int age){
super(name,age);
}
@Override
public void eat(){
System.out.println(name +"正在吃狗粮!!");
}
}
class Cat extends Animal{
public Cat(String name, int age) {
super(name, age);
}
@Override
public void eat(){
System.out.println(name +"正在吃猫粮!!");
}
}
// =======================================================
public class test2{
public static void main(String[] args) {
Dog dog = new Dog("小黄",12);
Cat cat = new Cat("小花",13);
dog.eat();
System.out.println("/");
cat.eat();
}
}
运行结果如下:
5.3 重写
5.3.1 规则
- 子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致
- 被重写的方法返回值类型可以不同,但是必须是具有父子关系的
- 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected
- 父类被static、private修饰的方法、构造方法都不能被重写。
- 重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心 将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法 构成重写.
5.3.2 设计原则
5.3.3 机制
5.4 向上转型和向下转型
5.4.1 向上转型
父类类型 对象名 = new 子类类型 ()
应用场景 :