封装的哲学思维:合理隐藏,合理暴露
封装最初的目的:提高代码的安全性和复用性,组件化
封装的步骤:
使用 private 修饰成员变量的原因:实现数据封装,不想让别人使用修改你的数据,比较安全
this 关键字的作用:
Java 是通过成员变量是否有 static 修饰来区分是类的还是属于对象的
按照有无 static 修饰,成员变量和方法可以分为:
成员变量:
成员方法:
成员变量的访问语法:
静态成员变量:只有一份可以被类和类的对象共享访问
实例成员变量:
成员方法的访问语法:
静态方法:有 static 修饰,属于类
实例方法:无 static 修饰,属于对象
public class Student {
// 1.静态方法:有static修饰,属于类,直接用类名访问即可!
public static void inAddr(){ }
// 2.实例方法:无static修饰,属于对象,必须用对象访问!
public void eat(){}
public static void main(String[] args) {
// a.类名.静态方法
Student.inAddr();
inAddr();
// b.对象.实例方法
// Student.eat(); // 报错了!
Student sea = new Student();
sea.eat();
}
}
内存问题:
栈内存存放 main 方法和地址
堆内存存放对象和变量
方法区存放 class 和静态变量(jdk8 以后移入堆)
访问问题:
继承是 Java 中一般到特殊的关系,是一种子类到父类的关系
继承的作用:
继承的特点:
继承的格式:
子类 extends 父类{
}
子类不能继承父类的东西:
public class ExtendsDemo {
public static void main(String[] args) {
Cat c = new Cat();
// c.run();
Cat.test();
System.out.println(Cat.schoolName);
}
}
class Cat extends Animal{
}
class Animal{
public static String schoolName ="seazean";
public static void test(){}
private void run(){}
}
继承后成员变量的访问特点:就近原则,子类有找子类,子类没有找父类,父类没有就报错
如果要申明访问父类的成员变量可以使用:super.父类成员变量,super指父类引用
public class ExtendsDemo {
public static void wmain(String[] args) {
Wolf w = new Wolf();w
w.showName();
}
}
class Wolf extends Animal{
private String name = "子类狼";
public void showName(){
String name = "局部名称";
System.out.println(name); // 局部name
System.out.println(this.name); // 子类对象的name
System.out.println(super.name); // 父类的
System.out.println(name1); // 父类的
//System.out.println(name2); // 报错。子类父类都没有
}
}
class Animal{
public String name = "父类动物名称";
public String name1 = "父类";
}
子类继承了父类就得到了父类的方法,可以直接调用,受权限修饰符的限制,也可以重写方法
方法重写:子类重写一个与父类申明一样的方法来覆盖父类的该方法
方法重写的校验注解:@Override
子类可以扩展父类的功能,但不能改变父类原有的功能,重写有以下三个限制:
继承中的隐藏问题:
public class ExtendsDemo {
public static void main(String[] args) {
Wolf w = new Wolf();
w.run();
}
}
class Wolf extends Animal{
@Override
public void run(){}//
}
class Animal{
public void run(){}
}
为什么子类构造器会先调用父类构造器?
class Animal {
public Animal() {
System.out.println("==父类Animal的无参数构造器==");
}
}
class Tiger extends Animal {
public Tiger() {
super(); // 默认存在的,根据参数去匹配调用父类的构造器。
System.out.println("==子类Tiger的无参数构造器==");
}
public Tiger(String name) {
//super(); 默认存在的,根据参数去匹配调用父类的构造器。
System.out.println("==子类Tiger的有参数构造器==");
}
}
为什么 Java 是单继承的?
答:反证法,假如 Java 可以多继承,请看如下代码:
class A{
public void test(){
System.out.println("A");
}
}
class B{
public void test(){
System.out.println("B");
}
}
class C extends A , B {
public static void main(String[] args){
C c = new C();
c.test();
// 出现了类的二义性!所以Java不能多继承!!
}
}
继承后 super 调用父类构造器,父类构造器初始化继承自父类的数据。
总结与拓展:
注意:
public class ThisDemo {
public static void main(String[] args) {
// 需求:希望如果不写学校默认就是”张三“!
Student s1 = new Student("天蓬元帅", 1000 );
Student s2 = new Student("齐天大圣", 2000, "清华大学" );
}
}
class Study extends Student {
public Study(String name, int age, String schoolName) {
super(name , age , schoolName) ;
// 根据参数匹配调用父类构造器
}
}
class Student{
private String name ;
private int age ;
private String schoolName ;
public Student() {
}
public Student(String name , int age){
// 借用兄弟构造器的功能!
this(name , age , "张三");
}
public Student(String name, int age, String schoolName) {
this.name = name;
this.age = age;
this.schoolName = schoolName;
}
// .......get + set
}
final 用于修饰:类,方法,变量
final 和 abstract 的关系是互斥关系,不能同时修饰类或者同时修饰方法
final 修饰静态成员变量,变量变成了常量
常量:有 public static final 修饰,名称字母全部大写,多个单词用下划线连接
final 修饰静态成员变量可以在哪些地方赋值:
定义的时候赋值一次
可以在静态代码块中赋值一次
public class FinalDemo {
//常量:public static final修饰,名称字母全部大写,下划线连接。
public static final String SCHOOL_NAME = "张三" ;
public static final String SCHOOL_NAME1;
static{
//SCHOOL_NAME = "java";//报错
SCHOOL_NAME1 = "张三1";
}
}
final 修饰变量的总规则:有且仅能被赋值一次
final 修饰实例成员变量可以在哪些地方赋值 1 次:
public class FinalDemo {
private final String name = "张三" ;
private final String name1;
private final String name2;
{
// 可以在实例代码块中赋值一次。
name1 = "张三1";
}
//构造器赋值一次
public FinalDemo(){
name2 = "张三2";
}
public FinalDemo(String a){
name2 = "张三2";
}
public static void main(String[] args) {
FinalDemo f1 = new FinalDemo();
//f1.name = "张三1"; // 第二次赋值 报错!
}
}
父类知道子类要完成某个功能,但是每个子类实现情况不一样
抽象方法:没有方法体,只有方法签名,必须用 abstract 修饰的方法就是抽象方法
抽象类:拥有抽象方法的类必须定义成抽象类,必须用 abstract 修饰,抽象类是为了被继承
一个类继承抽象类,必须重写抽象类的全部抽象方法,否则这个类必须定义成抽象类
public class AbstractDemo {
public static void main(String[] args) {
Dog d = new Dog();
d.run();
}
}
class Dog extends Animal{
@Override
public void run() {
System.out.println("跑");
}
}
abstract class Animal{
public abstract void run();
}
一、抽象类是否有构造器,是否可以创建对象?
抽象在学术上本身意味着不能实例化
public class AbstractDemo {
public static void main(String[] args) {
//Animal a = new Animal(); 抽象类不能创建对象!
//a.run(); // 抽象方法不能执行
}
}
abstract class Animal{
private String name;
public static String schoolName = "张三";
public Animal(){ }
public abstract void run();
//普通方法
public void go(){ }
}
二、static 与 abstract 能同时使用吗?
答:不能,被 static 修饰的方法属于类,是类自己的东西,不是给子类来继承的,而抽象方法本身没有实现,就是用来给子类继承
被继承,抽象类就是为了被子类继承,否则抽象类将毫无意义(核心)
抽象类体现的是"模板思想":部分实现,部分抽象,可以使用抽象类设计一个模板模式
//作文模板
public class ExtendsDemo {
public static void main(String[] args) {
Student xiaoMa = new Student();
xiaoMa.write();
}
}
class Student extends Template{
@Override
public String writeText() {return "\t内容"}
}
// 1.写一个模板类:代表了作文模板。
abstract class Template{
private String title = "\t\t\t\t\t标题";
private String start = "\t开头";
private String last = "\t结尾";
public void write(){
System.out.println(title+"\n"+start);
System.out.println(writeText());
System.out.println(last);
}
// 正文部分定义成抽象方法,交给子类重写!!
public abstract String writeText();
}
接口是 Java 语言中一种引用类型,是方法的集合。
**接口是更加彻底的抽象,**接口中只有抽象方法和常量,没有其他成分
修饰符 interface 接口名称{
// 抽象方法
// 默认方法
// 静态方法
// 私有方法
}
抽象方法:接口中的抽象方法默认会加上 public abstract 修饰,所以可以省略不写
静态方法:静态方法必须有方法体
常量:是 public static final 修饰的成员变量,仅能被赋值一次,值不能改变。常量的名称规范上要求全部大写,多个单词下划线连接,public static final 可以省略不写
public interface InterfaceDemo{
//public static final String SCHOOL_NAME = "张三";
String SCHOOL_NAME = "张三";
//public abstract void run();
void run();//默认补充
}
接口是用来被类实现的。
修饰符 class 实现类名称 implements 接口1,接口2,接口3,....{
}
修饰符 interface 接口名 extend 接口1,接口2,接口3,....{
}
实现多个接口的使用注意事项:
当一个类实现多个接口时,多个接口中存在同名的静态方法并不会冲突,只能通过各自接口名访问静态方法
当一个类实现多个接口时,多个接口中存在同名的默认方法,实现类必须重写这个方法
当一个类既继承一个父类,又实现若干个接口时,父类中成员方法与接口中默认方法重名,子类就近选择执行父类的成员方法
接口中,没有构造器,不能创建对象,接口是更彻底的抽象,连构造器都没有,自然不能创建对象
public class InterfaceDemo {
public static void main(String[] args) {
Student s = new Student();
s.run();
s.rule();
}
}
class Student implements Food, Person{
@Override
public void eat() {}
@Override
public void run() {}
}
interface Food{
void eat();
}
interface Person{
void run();
}
//可以直接 interface Person extend Food,
//然后 class Student implements Person 效果一样
jdk1.8 以后新增的功能:
public class InterfaceDemo {
public static void main(String[] args) {
// 1.默认方法调用:必须用接口的实现类对象调用。
Man m = new Man();
m.run();
m.work();
// 2.接口的静态方法必须用接口的类名本身来调用。
InterfaceJDK8.inAddr();
}
}
class Man implements InterfaceJDK8 {
@Override
public void work() {
System.out.println("工作中。。。");
}
}
interface InterfaceJDK8 {
//抽象方法!!
void work();
// a.默认方法(就是之前写的普通实例方法)
// 必须用接口的实现类的对象来调用。
default void run() {
go();
System.out.println("开始跑步");
}
// b.静态方法
// 注意:接口的静态方法必须用接口的类名本身来调用
static void inAddr() {
System.out.println("我们在武汉");
}
// c.私有方法(就是私有的实例方法): JDK 1.9才开始有的。
// 只能在本接口中被其他的默认方法或者私有方法访问。
private void go() {
System.out.println("开始。。");
}
}
参数 | 抽象类 | 接口 |
---|---|---|
默认的方法实现 | 可以有默认的方法实现 | 接口完全是抽象的,jdk8 以后有默认的实现 |
实现 | 子类使用 extends 关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。 | 子类使用关键字 implements 来实现接口。它需要提供接口中所有声明的方法的实现 |
构造器 | 抽象类可以有构造器 | 接口不能有构造器 |
与正常Java类的区别 | 除了不能实例化抽象类之外,和普通 Java 类没有任何区别 | 接口是完全不同的类型 |
访问修饰符 | 抽象方法有 public、protected 和 default 这些修饰符 | 接口默认修饰符是 public,别的修饰符需要有方法体 |
main方法 | 抽象方法可以有 main 方法并且我们可以运行它 | jdk8 以前接口没有 main 方法,不能运行;jdk8 以后接口可以有 default 和 static 方法,可以运行 main 方法 |
多继承 | 抽象方法可以继承一个类和实现多个接口 | 接口可以继承一个或多个其它接口,接口不可继承类 |
速度 | 比接口速度要快 | 接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法 |
添加新方法 | 如果往抽象类中添加新的方法,可以给它提供默认的实现,因此不需要改变现在的代码 | 如果往接口中添加方法,那么必须改变实现该接口的类 |
多态的概念:同一个实体同时具有多种形式同一个类型的对象,执行同一个行为,在不同的状态下会表现出不同的行为特征
多态的格式:
父类类型 对象名称 = new 子类构造器;
接口 对象名称 = new 实现类构造器;
多态的执行:
多态的使用规则:
多态的优势:
多态的劣势:
public class PolymorphicDemo {
public static void main(String[] args) {
Animal c = new Cat();
c.run();
//c.eat();//报错 编译看左边 需要强转
go(c);
go(new Dog);
}
//用 Dog或者Cat 都没办法让所有动物参与进来,只能用Anima
public static void go(Animal d){}
}
class Dog extends Animal{}
class Cat extends Animal{
public void eat();
@Override
public void run(){}
}
class Animal{
public void run(){}
}
基本数据类型的转换:
- 小范围类型的变量或者值可以直接赋值给大范围类型的变量
- 大范围类型的变量或者值必须强制类型转换给小范围类型的变量
引用数据类型的自动类型转换语法:子类类型的对象或者变量可以自动类型转换赋值给父类类型的变量
父类引用指向子类对象
public class PolymorphicDemo {
public static void main(String[] args){
Animal a = new Cat(); // 向上转型
Cat c = (Cat)a; // 向下转型
}
}
class Animal{}
class Cat extends Animal{}
instanceof:判断左边的对象是否是右边的类的实例,或者是其直接或间接子类,或者是其接口的实现类
public class Demo{
public static void main(String[] args){
Aniaml a = new Dog();
//Dog d = (Dog)a;
//Cat c = (Cat)a; 编译不报错,运行报ClassCastException错误
if(a instanceof Cat){
Cat c = (Cat)a;
} else if(a instanceof Dog) {
Dog d = (Dog)a;
}
}
}
class Dog extends Animal{}
class Cat extends Animal{}
class Animal{}