封装
广义的封装:广义的封装就是讲完成特定功能的方法抽取出来,例如银行程序中,检查密码的方法check()
封装的概念 封装:将类的某些信息隐藏在类内部,不允许外部程序直接访问, 而是通过该类提供的方法来实现对隐藏信息的操作和访问 。
封装的好处:
只能通过特定的方式访问
隐藏类的实现细节,且方便修改实现
方便加入控制语句
对属性的封装:
public class Human {
private String name;//将变量私有化不能随意进行访问
//对私有变量进行操作,为改变成员变量给出方法
public void setName(String name){
if (name.length()<6&name.length()>1) {
this.name = name;
}else{
System.out.println("请输入长度合适的名字");
}
}
public String getName(){
return name;
}
}
//测试代码
Human zs=new Human();
//此时对象zs无法直接访问属性System.out.println(zs.name);
//但是可以通过方法访问
zs.setName("张三");
System.out.println(zs.getName());
对方法的封装:
此处引入一个单例模式的问题,即如果程序中只需要一个对象,不允其他地方随意的创建对象的问题
第一种解决方法(懒汉单例):
public class WindowDemo {
/*
懒汉单例
在外界获取对象引用时才创建对象
*/
static WindowDemo windowDemo;//声明一个静态的对象应用记录创建的唯一一个对象
/*
对方法进行私有化
*/
//构造方法私有化,无法直接创建对象
private WindowDemo(){
}
//由于无法创建对象,故提供一个静态的成员方法创建对象
public static WindowDemo getWindowDemo(){
if (windowDemo==null){
windowDemo=new WindowDemo();
}
return windowDemo;
}
}
第二种解决方法(饿汉单例):
public class WindowDemo1 {
/*
饿汉单例(急切式单例)
类加载时就创建对象
*/
static WindowDemo1 windowDemo1=new WindowDemo1();//类加载时直接创建唯一的对象
/*
对方法进行私有化
*/
//构造方法私有化,无法直接创建对象
private WindowDemo1(){
}
//由于无法创建对象,故提供一个静态的成员方法创建对象
public static WindowDemo1 getWindowDemo(){
return windowDemo1;
}
}
以上对属性与方法的封装核心思想都是按需求将某些信息隐藏起来,再提供一个方法给外界来调用,在这方法中就可以增加控制语句来访问被隐藏的信息,这种方式即方便修改细节也能满足特定的需求
继承
概念:
继承是从类中派生出一个新的类叫做子类,继承的类叫做子类,被继承的类叫做父类,子类会吸收父类中所有的非私有的属性与方法,并且能拓展自己特有的功能,继承是实现代码复用性的根基,是实现代码拓展性的主要途径
子类若是继承父类有两个叫法:
例如:1.Dog类是Animals类的子类,Animals类是Dog类的父类
2.Dog类是Animals类的派生类,Animals类是Dog类的基类
注:
extends关键字表示继承
语法格式: [访问权限修饰符] [修饰符]子类名 extends 父类名{子类体}
一个子类只能继承于一个直接父类,Java中不支持多继承
继承之后的子类可以调用父类的所有的非私有的属性与方法
继承的子类如果被调用后此时父类没有加载,则此时优先加载父类
使用场景:
符合is-a的设计准则(继承),//其他准则有has-a 关联,use-a 依赖
子类共有的属性和方法可以放到父类中去
传递性:
若B类继承于A类,C类继承于B类,则C类会继承A类与B类所有的成员属性与成员方法
//定义一个Person类
public class Person {
//可以定义类中的属性与方法,子类可以继承出去
}
/*
定义Person类的子类
一个子类只能有一个直接的父类
*/
public class Student extends Person {
}
/*
定义Student类的子类
*/
public class CollegeStudent extends Student {
}
继承中的构造方法
在继承的子类中,由于子类会需要用到父类的属性与方法,但是为了避免子类在创建对象时将父类的对象也创建一遍造成内存浪费,故在加载时会将父类中的属性与方法加载在子类的对象中,此时创建子类的对象时就会调用父类的构造方法
super关键字
super关键字可以调用父类的属性与方法,但是与this不同的是super并不代表父类
使用super关键字访问父类成员的方法:
• 用super.成员变量名来引用父类成员变量 • 用super.方法名(参数列表)访问父类的方法。 • 用super.构造方法(参数列表)访问父类构造方法 例如:super()代表调用父类的无参构造方法
在类的继承中,在子类的构造方法的第一行一定会有一个super关键字调用父类的构造方法,这个构造方法有参或者无参取决于super()方法中的参数,若是没有显式的写出super关键字,则系统会默认自动的在构造方法第一行加入super()代码来自动调用父类的无参构造方法
public class Person {
String name;
int age;
//构造方法
public Person() {
//若不写super关键字系统也会默认添加用用来调用父类无参的构造方法
System.out.println("Person类无参的构造方法");
}
public Person(String name,int age){
super();//不写也会默认存在一个调用父类无参的构造方法
this.age=age;
this.name=name;
System.out.println("Person类有参的构造方法");
}
}
public class Student extends Person {
double mark;
//无参构造方法
public Student() {
//若不写super关键字系统也会默认添加用用来调用父类无参的构造方法
System.out.println("Student类中无参的构造方法");
}
//有参的构造方法
public Student(String name,int age, double mark){
super(name,age);
this.mark=mark;
System.out.println("Student类有参的构造方法");
}
}
public class CollegeStudent extends Student {
//无参的构造方法
public CollegeStudent() {
//super();//这一句不写系统也会默认添加用用来调用父类无参的构造方法
System.out.println("CollegeStudent类无参的构造方法");
}
//有参的构造方法
public CollegeStudent(String name, int age, double mark) {
super(name, age, mark);
System.out.println("CollegeStudent类有参的构造方法");
}
}
//此时创建CollegeStudent类的对象会自动调用Person类与Student类的构造方法
继承中构造方法的调用顺序:
子类对象创建时构造方法调用饿顺序为:从最高级的父类的构造方法开始调用,依次往下直到调用到当前类的构造方法
方法的重写
父类中定义的方法都会被子类继承,但有些继承的有些方法中父类定义的方法并不满足子类的需求,故java中提供了一种机制给子类去修改父类的方法,这种机制就叫做方法的重写
注意:方法重写的条件是子类重写父类中继承类的方法,若是父类中没有此方法就不构成方法的重写了
构造方法,静态方法不能重写,成员变量不存在重写
@Override注解代表方法的重写,在没有此注解的情况下方法可以在子类中被重载
方法重写的规则:
• 方法名相同、参数列表相同; • 返回值类型相同; • 访问权限不能小于父类权限;
//父类
public class Student extends Person {
public void study(){
System.out.println(this.getName()+ "在学习");
}
}
//子类
public class CollegeStudent extends Student {
//方法重写
@Override
public void study(){
System.out.println("大学生"+this.getName()+ "在学习");
}
public void xieLunWen(){
this.study();//父类的方法本来不能使用this调用,重写之后可以使用this调用
System.out.println("大学生需要写论文");
}
}
抽象类
抽象方法
由方法的重写可以看出,子类中的方法有时有独立的需求,但其实很多时候,父类的方法并不匹配子类的需求,当子类都有独立的需求的时候,父类中的方法体就不必要写了,这时可以将父类中的方法定义为抽象方法
注: 抽象方法是一种特殊的方法:它只有声明,而没有具体的实现. 抽象方法必须用abstract关键字进行修饰.(作为修饰符,修饰类或者方法)
抽象类的定义
● 如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
● 抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员法和构造方法。
● 用abstract修饰的类就是抽象类。如果某个类中包含有抽象方法,那么该类就必须定义成抽象类。
特点:
1.抽象类不能创建实例对象,但是可以有构造方法,因为抽象类中的方法没有实现,所以不能创建对象来调用方法
2.抽象类只能作为基类,继承抽象类的非抽象类必须实现其中的所有抽象方法,且需要按重写的要求实现。否则,该类也必须声明为抽象类。
//定义抽象类
public abstract class Animals {
public abstract void eat();//定义抽象方法
}
//定义子类
public class Dog extends Animals{
//重写父类中的抽象方法
@Override//该注解代表方法的重写
public void eat() {
System.out.println("狗站着吃饭");
}
}
多态
概念:多态是指同一种事物,在不同的时刻表现出来不同的状态(此处同一事物指同一父类,不同的状态表示不同的子类)
多态形成的条件:
1.要有继承关系的出现,即需要有两个类分别作为父类和子类
2.要有方法的重写(这里特指的是抽象的方法,重写之后,调用时实现的是子类重写后的方法)
3.父类的引用指向子类的对象(例Animals dog=new Dog())(当编译期类型是父类,运行期类型是子类时,被称为父类引用指向子类对象)
注:当使用指向子类对象的父类引用去调用父类中的成员变量与静态方法时编译与运行都是采用的父类
//父类
public abstract class Animals {
//父类的成员变量
String string="父类的成员变量";
//定义抽象方法
public abstract void eat();
//定义成员方法
public void sleep(){
System.out.println("动物在睡觉");
}
//父类的静态方法
public static void show(){
System.out.println("动物类");
}
}
//子类
public class Dog extends Animals {
String string="狗类的成员变量";
@Override
public void eat() {
System.out.println("狗吃骨头");
}
//定义狗类中特有的方法
public void play(){
System.out.println("狗会玩");
}
//子类中的静态方法
public static void show(){
System.out.println("狗类");
}
}
//测试类
public class Test {
public static void main(String[] args) {
Animals a1=new Dog();//父类的引用去调子类的对象
a1.eat();//运行结果:狗吃骨头,编译期间类型为父类,运行期间为狗类
a1.sleep();
//a1.play();//不通过编译,编译期间是父类,父类中没有子类特有的方法
a1.show();//静态方法,编译与运行都是父类调用
System.out.println(a1.string);//成员变量,编译运行都是父类调用
}
}
多态的优点:
提高了代码的可拓展性
//以上述代码为模板多增加两个类
//使用非多态的方法来实现饲养动物
public class Test3 {
/*
模拟饲养员喂动物
*/
public static void main(String[] args) {
Test3 t=new Test3();
Dog dog=new Dog();
Birds birds=new Birds();
Cat cat=new Cat();
t.feedDog(dog);
t.feedBirds(birds);
t.feedCat(cat);
}
//饲养员喂狗
public void feedDog(Dog dog){
dog.eat();
}
//饲养员喂鸟
public void feedBirds(Birds birds){
birds.eat();
}
/*
此时对应类的方法都是写好的,如果要多添加一个类则还需要多添加一个方法
例如多添加一个喂猫
*/
public void feedCat(Cat cat){
cat.eat();
}
}
//使用多态的方法来实现饲养动物
public class Test2 {
public static void main(String[] args) {
Test2 t=new Test2();
Animals dog=new Dog();//这里定义子类的引用也可以使用多态的方法调用子类的中特有的方法
Animals birds=new Birds();//这里相当于子类的类型向上转型为了父类类型
Animals cat=new Cat();
t.feedAnimals(dog);
t.feedAnimals(birds);
t.feedAnimals(cat);
}
public void feedAnimals(Animals animals){
animals.eat();//多态只需要定义一个方法,传入父类类型或者子类类型的引用就可以调用子类中的方法
/*
多态的不足
多态由于使用的都是父类的类型
但是如果需要使用子类中特有的方法,就无法使用父类类型调用了,虽然可以使用强制类型转换解决引用向下兼容的问题,但是在多态的方法中无法兼顾所有的子类
解决办法:使用instanceof关键字:可以判断引用是否是某一类的引用
*/
if(animals instanceof Dog){
((Dog) animals).play();
}
}
}
向上转型问题与向下转型问题:
向上转型:父类引用指向子类对象就是向上转型,子类对象自动转换为了级别更高的父类引用
向下转型:可以将父类的对象强行转换成子类的引用,再用子类的引用去调用子类的方法
多态的不足:
多态由于使用的都是父类的类型但是如果需要使用子类中特有的方法,就无法使用父类类型调用了,虽然可以使用强制类型转换解决引用向下兼容的问题,但是在多态的方法中无法兼顾所有的子类 解决办法:使用instanceof关键字:可以判断引用是否是某一类的引用
final关键字
final关键字用于修饰类,属性,方法
修饰类:final修饰的类不允许被其他类继承,例如String类
修饰属性:final修饰的属性不能被修改,且必须要被初始化
final修饰属性初始化的两种方法:
1.在定义时直接赋值,且由于各对象都会初始化该属性,故使用static修饰在内存中只存储一份
2.在构造方法中为其初始化,利用无参或者有参的构造方法都可以初始化,但是必须为其赋值
两者的使用场景:若是所有对象共用则使用第一种,若每个对象不同则使用第二种
修饰方法:被修饰的方法无法被重写
/*final修饰类
被修饰的类不允许被继承
*/
//public final class FinalDemo {
public class FinalDemo{
/*
final修饰属性
被修饰的类不能再对其的值进行更改了,且一定要对其进行初始化
初始化的方式有两种
1.直接在定义式进行赋值
2.利用构造方法进行初始化
若是在定义时未进行初始化,则必须在构造方法中进行初始化赋值,有参无参的第可以
*/
public final static int NUM=1;//若是每个对象都需要一个相同的常量则用static定义,只存一份在内存中
public final int SUM;
//使用无参的构造方法初始化
public FinalDemo() {
this.SUM=0;
}
//使用一个有参的构造方法初始化
public FinalDemo(int SUM) {
this.SUM = SUM;
}
}
接口
概念:接口是一种规范,可以理解为一种比较纯正的抽象类,它是作为设计层面来使用的,主要功能是定义功能和方法,具体的实现交给具体的类
以USB接口为例对照Java中的接口
接口的定义与使用:
定义:[访问权限修饰符] interface 接口名称 [extends 接口名称1,接口名称2,接口名称3……]{
//定义接口中的常量,抽象方法,静态方法,默认方法
}
使用:接口与继承关系不同,类与接口的关系是实现,使用implement关键字
[访问权限修饰符] [修饰符]class 类名称 implement 接口名称[,接口名称1,接口名称2,接口名称3……]{
}
结合继承:一个类可以在继承一个父类的同时实现多个接口
[访问权限修饰符] [修饰符]class 类名称 extends 父类名称 implement 接口名称[,接口名称1,接口名称2,接口名称3……]{
}
接口的特性:
1.接口中的属性默认都是被public static final修饰的静态常量
2.接口中的方法默认都是被public abstract修饰的抽象方法
3.jdk8之后接口加入了静态方法,默认方法,以上四种是接口中所能定义的所用内容
4.类与接口的关系是实现,一个类可以实现多个接口,实现接口的类要么是抽象类,要么实现接口中所有的抽象方法
5.接口也可以继承多个接口
6.由于接口中有抽象方法,故接口不能被实例化,但是可以直接使用接口调用常量
7.接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字
8.与继承关系类似,接口与实现类之间存在多态性
//接口可以继承多个接口
public interface MyInterface extends MyInterfaceA,MyInterfaceB,MyInterfaceC {
//接口中的成员变量默认都是public修饰的静态常量
int NUM=10;
//接口中的方法默认为public abstract修饰的抽象方法
public abstract void eat();
void sleep();
//jdk8之后接口中加入的静态方法
public static void show(){
System.out.println("接口中的静态方法");
}
//jdk8之后接口中加入的默认方法
public default void show1(){//default必须要显式的写出来
System.out.println("接口中的默认方法");
}
}
//被继承的接口1
public interface MyInterfaceA {
void testA();
}
//被继承的接口2
public interface MyInterfaceB {
void testB();
}
//被继承的接口3
public interface MyInterfaceC {
void testC();
}
//接口的实现类
public class MyInterfaceImpl implements MyInterface {
@Override
public void eat() {
}
@Override
public void sleep() {
}
@Override
public void testA() {
}
@Override
public void testB() {
}
@Override
public void testC() {
}
}
接口与抽象类的相同与差异:
相同:
1.接口与抽象类都是抽象的,设计层面上的,其中都含有未实现的功能
2.接口与抽象类都不能被实例化
3.接口与抽象类都是被其他类来继承或者实现的,且子类的定义格式类似
4.都存在多态性
差异:
抽象类:
1.抽象类中除了抽象方法外还可以定义成员变量,成员方法,构造方法,代码块等
2.一个类只能继承一个抽象类,一个抽象类也只能继承一个抽象的类
接口:
1.接口中除了抽象方法之外,还可以定义静态常量,默认方法与静态方法
2.接口中不能定义构造方法,接口是一个单纯的抽象功能没有构造方法代表着无法加载接口中的属性到对象中去,只能实现接口中的方法
3.一个类可以实现多个接口,一个接口可以继承多个接口