面向对象
概念解析
什么是面向对象?
开发过程中,以声明的标准或者逻辑编写程序
1.面向对象——>(老板)
对象就是打工人,什么人面向打工人呢——>当然是老板
2.面向过程(打工人)
例如:《张三想开门》
面向过程的逻辑是:我起身离开座位——>走到门前——>开门
面向对象的逻辑是:我叫张三去开门,张三重复上面的过程。
具体解释
面向对象:只需要负责使用该对象,不需要关系对象的具体执行逻辑,所以方便使用者去调用。比较擅长完成负责的业务功能。强调的使用或者调用。
面向过程:所有执行步骤都是按部就班的,在整个逻辑中不能随意修改步骤,不然就可能出现问题。但是它是解决问题的最基础的方案。强调的是完成。
面向对象的具体解释
对象和类的概念解释
类:一个模板(类似于图纸),通过这个图纸可以源源不断产生对象,用图纸可以批量造出东西(即对象),例如:用飞机蓝图不断的可以造出飞机
对象:具体的事物,;比如飞机 ;飞机就是一个对象
(类似于先有鸡还是先有蛋):不确定
如果按照 代码构建 的角度思考:
先有对象,通过将多个对象的共性抽取出来,将其归为一类; 这个角度我们发现先有对象 才有的类
如果按照 编写代码 的角度思考:
先有类,再有对象。定义类之后,通过`类名 变量名 = new 类名()` 创建一个类的实例独享。
类和对象的声明
声明 类:
将当前 类 可能产生的对象的相同点罗列出来:
外在的表现————>定义成为类中的变量
内在的功能————>定义成为类中的一个方法 ,这个方法不能加static的关键词的
public class Computer {
/* 属性 */
int price;
String type;
String color;
/* 功能 */
public void playGame(){
System.out.println("玩游戏");
}
public void info(){
System.out.println("price:"+price);
}
}
声明 对象:
声明对象的方式:类名 变量名 = new 类名()
对象.属性
对象.方法
public class ComputerTest {
public static void main(String[] args) {
Computer c1 = new Computer();
c1.price = 123456;
c1.playGame();
c1.info();
}
}
java中的变量,局部、成员、 静态、 常量
局部变量:
概念:在方法内声明的变量被称为局部变量,该变量只能在该方法内使用,类中的其他方法并不知道该变量
注意事项:
局部变量声明在方法、构造方法或者语句块中。
局部变量在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,将会被销毁。
访问修饰符不能用于局部变量。
局部变量只在声明它的方法、构造方法或者语句块中可见。
局部变量是在栈上分配的。
局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。
成员变量:
概念:在类内部但在方法体外声明的变量,称为成员变量,或者实例变量。之所以称为实例变量,是因为该变量只能通过类的实例(对象)来访问。
注意事项:
成员变量声明在一个类中,但在方法、构造方法和语句块之外。
当一个对象被实例化之后,每个成员变量的值就跟着确定。
成员变量在对象创建的时候创建,在对象被销毁的时候销毁。
成员变量的值应该至少被一个方法、构造方法或者语句块引用,使得外部能够通过这些方式获取实例变量信息。
成员变量可以声明在使用前或者使用后。
访问修饰符可以修饰成员变量。
成员变量对于类中的方法、构造方法或者语句块是可见的。一般情况下应该把成员变量设为私有。通过使用访问修饰符可以使成员变量对子类可见;成员变量具有默认值。数值型变量的默认值是 0,布尔型变量的默认值是 false,引用类型变量的默认值是 null。变量的值可以在声明时指定,也可以在构造方法中指定。
静态变量:
概念:通过static关键字声明的变量被称为静态变量(类变量),它可以直接被类访问
注意事项:
静态变量在类中以 static 关键字声明,但必须在方法构造方法和语句块之外。
无论一个类创建了多少个对象,类只拥有静态变量的一份拷贝。
静态变量除了被声明为常量外很少使用。
静态变量储存在静态存储区。
静态变量在程序开始时创建,在程序结束时销毁。
与成员变量具有相似的可见性。但为了对类的使用者可见,大多数静态变量声明为 public 类型。
静态变量的默认值和实例变量相似。
静态变量还可以在静态语句块中初始化。
常量:
概念:有些数据的值不会发生改变的,这些数据被叫做常量——使用final关键字修饰的成员变量。
构造器
构造器是一个特殊的方法:懒人方法
构造器的主要作用:完成对新对象的初始化(即给对象赋值)。
所以说这个对象的是已经创建好了,空间已经有了,构造器只是给对象赋值的。
简化了代码格式
public class Class003_Elephant {
public static void main(String[] args) {
//没用构造器之前,是这样赋值的↓↓ 一个一个的赋值,且相互之间没有联系。
Elephant ele = new Elephant();
ele.name="胖胖";
ele.type = "非洲象";
ele.color = "粉色";
//当你想 一次性 输出一个学生的编号,姓名,年龄...的时候,构造器就有了用武之地了。
class Elephant{
public String name; public String type; public String color;
}
// ps: 构造器的名称必须与类名相同
// 构造器的作用是初始化
public class Class003_StudentTest {
public static void main(String[] args) {
//这就是使用了构造器的样子,超级偷懒的
Student s1 = new Student(1001, "张三", 18);
Student s2 = new Student(1002, "李四", 18);
}}
class Student {
public int id;
public String name;
public int age;
//这里是————————————————————————————↓↓↓构造器
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
多个构造器
package Test.test001;
public class Class001_test {
public static void main(String[] args) {
Person p1 = new Person("薛师傅","爱吃炸鸡",18);//第一个构造器起了作用
Person p2 = new Person("薛师傅")//第二个构造器起了作用,第一个就没用到
}
}
class Person{
String name; String love; int age;
//第一个构造器:
public Person(String Pname,String Plove,int Page){
age = Page ; name = Pname; love = Plove;
}//第二个构造器:
public Person(String Pname){
name = Pname;
}
}
}
无参构造器
(即无参数)
一旦定义了自己的构造器,默认的构造器(即无参构造器)就被覆盖了,就不能使用默认的无参构造器了
即不能使用 Person p1 = new Person()这样定义对象了
package Test.test001;
public class Class001_test {
public static void main(String[] args) {
Person p1 = new Person();//【2】除非你再显示定义一下就可以用了
}
}
class Person{
String name; String love; int age;
//【3】显示的定义无参构造器
Public Person(){
}
// 【1】一旦定义了自己的构造器,默认的构造器(即无参构造器)就被覆盖了,就不能使用默认的无参构造器了
// 即不能使用 Person p1 = new Person()这样定义对象了
public Person(String Pname){
name = Pname;
}
}
}
1: 这个方法 没有返回值 也不需要编写返回值类型,更不需要写return语句,return语句可以用来停止方法。
2:这个方法的方法名称和当前的类是同名
3: 这个方法默认情况下在每个类中都存在默认的空参数的方法。称之为默认构造器
4:每个类中如果存在带参构造器,那么原本的空构造器就不存在了,如果你想让他存在,自己需要手动的显式定义空构造器。构造器是支持重载的
5: 构造器之间要互相调用不能直接通过方法名调用,必须要使用this()进行调用
流程分析(加了构造器)
class Person{
int age = 90; String name;
Person(String a,int b){
name = a; age = b;
}}
Person p = new Person("小薛",18);
Person p = new Person("小薛",18);
**1、**new的时候,在堆中开辟了一个空间,空间的地址是0x11。会有一个默认值在空间里面.
int类型:0 String类型:null double类型:0.0
**2、**开完之后,开始初始化对象,把默认值替换为你赋的值。例如(age=90),把90赋值给age。而name没有赋值则还是为null。
**3、 ** 最后到执行构造器时,把小薛传给a,把18传给b,形参ab再把值赋给name 和 age,通过这样实现赋值。然后把对象在堆中的地址返回给 p
this关键字
this. 代表当前对象(谁在调用构造器,当前对象指的就是谁)
this关键字可以用来访问本类的属性 方法 构造器
this用于区别当前类的属性和局部变量
this不能在类定义的外部使用,只能在类定义的方法中使用(因为this是跟对象关联的)
class Person{
int age = 90;
String name;
Person(String name,int age){
this.name = name; //当前对象的 属性name
this.age = age; //当前对象的 属性age
// name = Pname;
// age = Page;
}
public void f1(){
String name = "smith";//传统方式会根据就近原则找到这个 smith。而不会去找你new的对象的值 小薛
//传统方式:
System.out.println("name"+name+"age"+age);
//使用this访问属性:而this会非常准确的找到你对象的值
System.out.println("name"+this.name+"age"+this.name);
}
}
Person p = new Person("小薛",18);
访问成员方法的语法:this.方法名(参数列表)
this.f1()//this调用
访问构造器语法:this(参数列表)注意只能在构造器中使用
(只能在构造器中使用(即只能在构造器中访问另外一个构造器))
class T1{
public T1(){
//这里是去访问T1(String name ,int age)
this("jack",100);//this语句一定要处于第一条语句
System.out.println("T() 构造器");
}
public T1(String name,int age){ //被访问了
System.out.println("T(String name,int age)构造器");
}
}
final关键字
对于变量:final关键字可以用来修饰变量,表示该变量的值一旦被初始化后便不能再被修改。一般使用全大写字母来表示final变量。
对于方法:final关键字可以用来修饰方法,表示该方法不能被子类重写。
对于类:final关键字可以用来修饰类,表示该类不能被继承。
代码块
**理解:**相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
场景:如果有个构造器中都有重复语句,可以抽取到初始化块中,提高代码的重用性
快速入门:
构造块是先于构造器被调用的
//解读:下面三个构造器都有相同的语句,这样代码看起来比较冗余
//解决:我们可以把相同的语句,放到一个代码块中,即可。
// 这样当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容
{
System.out.println("电影屏幕打开...");
System.out.println("广告开始了");
System.out.println("电影正式开始...");
}
class Movie{
public String name;
public double price;
public String director;
{
System.out.println("电影屏幕打开...");
System.out.println("广告开始了");
System.out.println("电影正式开始...");
}
//3个构造器===》重载
public Movie(String name) {
/* System.out.println("电影屏幕打开...");
System.out.println("广告开始了");
System.out.println("电影正式开始..."); */
this.name = name;
}
public Movie(String name, double price) {
/* System.out.println("电影屏幕打开...");
System.out.println("广告开始了");
System.out.println("电影正式开始..."); */
this.name = name;
this.price = price;
}
public Movie(String name, double price, String director) {
/* System.out.println("电影屏幕打开...");
System.out.println("广告开始了");
System.out.println("电影正式开始..."); */
this.name = name;
this.price = price;
this.director = director;
}
}
static
静态代码块作用:对类进行初始化(跟对象无关),而且随着类的加载而执行,并且只会执行一次。如果是非静态代码块(就上面的那种),每创建一个对象,就执行一次。
(类加载指的是将class文件读入内存,只会执行一次)
静态方法:
当我们在一个方法前添加了static关键字,它就成为了一个静态方法。静态方法属于类,不属于实例对象。它们在类加载时就已经存在了,可以直接使用,无需创建对象。我们可以通过类名直接调用静态方法,而不需要创建类的实例对象。
public class MyClass {
public static void staticMethod() {
System.out.println("This is a static method");
}
}
MyClass.staticMethod(); // 调用静态方法
静态变量:
当我们在变量前添加了static关键字,它就成为了一个静态变量。静态变量属于类,不属于实例对象。它们在类加载时就已经存在了,可以直接使用,无需创建对象。静态变量可以被类的所有实例对象共享。
public class MyClass {
public static int staticVariable = 10;
}
MyClass obj1 = new MyClass();
MyClass obj2 = new MyClass();
System.out.println(obj1.staticVariable); // 输出 10
System.out.println(obj2.staticVariable); // 输出 10
obj1.staticVariable = 20;//被重新赋值后就改变了
System.out.println(obj1.staticVariable); // 输出 20
System.out.println(obj2.staticVariable); // 输出 20
静态代码块:
当我们在类中使用static关键字定义了一个代码块,它就成为了一个静态代码块。静态代码块在类加载时执行,并且只执行一次。
public class MyClass {
static {
System.out.println("This is a static block");
}
}
MyClass obj = new MyClass(); // 输出 This is a static block
静态内部类:
当我们在类中使用static关键字定义了一个内部类,它就成为了一个静态内部类。静态内部类不依赖于外部类的实例对象,可以直接访问外部类的静态成员和方法。
public class OuterClass {
static class InnerClass {
public static void staticMethod() {
System.out.println("This is a static method ");
}
}
}
OuterClass.InnerClass.staticMethod(); // 调用静态方法
类什么时候被加载【重要】:
创建对象实例时(new)
创建子类对象实例,父类也会被加载,而且父类先被加载,子类后加载
class AA extends BB{
//静态代码块
static {
System.out.println("AA 的静态代码1被执行...");
}
}
//父类
class BB {
//静态代码块
static {
System.out.println("BB 的静态代码1被执行...");
}
}
使用类的静态成员时(静态属性,静态方法)
System.out.println(Cat.n1);
class Cat{
public static int n1 = 317;
}
创建一个对象是,在 一个类 调用的顺序(重难点)
1、调用静态代码块和静态属性初始化(优先级最高)静态的跟类相关,类一般都是最先被加载的
(注意:静态代码块 和 静态属性初始化 调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按题目定义的顺序调用)
2、调用普通代码块和普通属性的初始化
(注意:普通代码块 和 普通属性初始化 调用的优先级一样,如果有多个普通代码块和多个普通变量初始化,则按题目定义的顺序调用)
3、调用构造器(优先级最低)
4、构造器的最前面其实隐含了super()调用普通代码块(优先于构造器和普通代码块)
A a1 = new A();
//输出顺序是:
getN1被调用...
静态代码块被调用了
getN2被调用...
普通代码块被调用了
A() 被调用
public class CodeBlockDetail02 {
public static void main(String[] args) {
A a1 = new A();
}
}
class A{
//普通属性的初始化↓
public int n2 = getN2();
{ //普通代码块↓
System.out.println("普通代码块被调用了");
}
public int getN2(){
System.out.println("getN2被调用...");
return 200;
}
//静态属性的初始化
public static int n1 = getN1();
static {//静态代码块
System.out.println("静态代码块被调用了");
}
public static int getN1(){
System.out.println("getN1被调用...");
return 100;
}
//构造器
public A(){
//底层代码中这里有隐藏的执行要求
//1、super()
//2、调用普通代码块
System.out.println("A() 被调用");
}
}
出现继承关系,他们的静态代码块、静态属性初始化、普通代码块、普通属性初始化、构造方法的调用顺序如下:
父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
父类的构造器
子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
子类的构造器
访问修饰符
公开级别:用public修饰,对外开放
受保护级别:用protected修饰,对子类和同一个包(同文件夹下)中的类公开
默认级别:没有修饰符号,向同一个包的类公开
私有级别:用private修饰,只有类本身可以访问,不对公开
![](https://i-blog.csdnimg.cn/blog_migrate/51fc0ff0458dcec84a84eb7e453a799a.png)
修饰符可以用来修饰类中的 属性, 成员方法以及 类
只有 默认的和public才能修饰类,并且遵循上述的访问规则
成员方法的访问规则和属性完全一样
封装
官方:封装就是把抽象出的数据(即属性)和对数据的操作(即方法)封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法],才能对数据进行操作。
好处:1、隐藏了实现的细节。2、可以对数据进行验证,保证安全合理
实现3步骤:主题思想就是在方法内加入限制条件
私有化:就是要求别人不能直接处理,例如private int num; 加了private,就不能直接赋值给num(即num = 317)而是需要满足方法内设置的一些条件。
将属性私有化private(使之不能直接修改属性)
提供一个公共的(public)方法,例如set方法,由于对属性判断并赋值
public void setXxx(类型 参数名){// Xxx 表示某个属性
//加入数据验证的业务逻辑
属性 = 参数名
}
3.提供一个公共的(public)t方法,例如get方法,用于获取属性的值
public 数据类型 getXxx() {// 权限判断 Xxx 某个属性
return xx;
}
//部分代码,详情找 代码.md
fuzhi(){
}
fuzhi(String name,double code,String pwd){
this.setName(name);
this.setCode(code);
this.setPwd(pwd);
}
public String getName() {
return name;
}
//名字
public void setName(String name) {
if (name.length()>=2 && name.length()<=4 ){
this.name = name;
}else{
System.out.println("您输入的名字格式错误!");
}
}
继承(重点)
为什么需要继承:降低写代码的重复性!
继承的基本语法:
class 子类 extends 父类{
}
子类会自动拥有父亲定义的属性和方法
父类又叫 超类 基类。
子类又叫派生类。
概念图:
![](https://i-blog.csdnimg.cn/blog_migrate/fc28960874c575cc42d20b7ce9661c10.png)
案例:
他两的父类:
![](https://i-blog.csdnimg.cn/blog_migrate/cc27f821b99b7f714c3fd2f7c8dd550c.png)
大学生类:
![](https://i-blog.csdnimg.cn/blog_migrate/208e9edccc3f98eff22109a5cb857bda.png)
小学生类:
![](https://i-blog.csdnimg.cn/blog_migrate/08910aa76e21a4183d3a4e6816c29763.png)
测试:
![](https://i-blog.csdnimg.cn/blog_migrate/4de687c6e8e3574b2bbb5d0e5dd4914d.png)
细节问题:
1、子类继承了所有的属性和方法(不继承父类的构造方法,但可以调用),但是私有属性和方法不能在子类直接访问,要通过父类公共的方法去访问,非私有的属性和方法可以在子类中直接访问
![](https://i-blog.csdnimg.cn/blog_migrate/fc7192c797a3bae5df33cf16f55b7a6e.png)
调用私有private的属性,使用公共的public方法曲线救国调用。
方法也是一样。
![](https://i-blog.csdnimg.cn/blog_migrate/085642d2b679e249773f38a64119ebff.png)
注意事项:
1.子类必须调用父类的构造器,完成父类的初始化
![](https://i-blog.csdnimg.cn/blog_migrate/7540203b77497ee5822392692707226b.png)
2.当创建子类对象时,不管子类使用的哪个构造器,默认情况下总会去调用父类的无参构造器。
3.如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定父类的哪个构造器完成对父类的初始化工作,否则编译不会通过。
![](https://i-blog.csdnimg.cn/blog_migrate/a8529e9f4964c7a176363ab652df6e8a.png)
4.如果希望指定去调用父类的某个构造器,则显示的调用一下:super(参数列表),上面的图有提到
5.super在使用时,需要放在构造器第一行。
6.super()和this()都能放在构造器的第一行,因此两个方法不能共存在一个构造器里。
7.(仅了解)java所有的类都是object类的子类,object是所有类的基类
8.(仅了解)父类构造器的调用不限于直接父类!将一直网上追溯指导object类(顶级父类)
9.子类最多只能继承一个父类(指直接继承),即一个好大儿就只能有一个父亲 , java中是单继承机制,不能有干爹
继承的本质分析:(还没看完 在p294)
方法重写/覆盖(override)
基本介绍:
方法重写/覆盖就是子类有一个方法,和父类的某个方法名称、返回类型、参数一样,那么我们就说子类的方法覆盖了父类的方法,但是父类的方法并没有改变,可以通过super调用
public void aaa(){ //父类中的方法
System.out.println("我有一个好大儿")
}
//子类中的方法
public void aaa(){
super.aaa();//调用父类中的方法
System.out.println("世间无我这般人")
}
//最后输出的是子类方法中的 "世间无我这般人",父类中的被覆盖了。
需满足的条件:
子类方法的 方法名称 参数 == 父类方法的 方法名称 参数
子类的返回类型和父类的返回类型一样,或者是父类返回类型的子类
比如:父类的返回类型是object ,子类方法的返回类型是String 这样也是允许的。
(儿子永远是儿子,爹还是你爹)
子类的方法不能缩小父类方法的访问权限
![](https://i-blog.csdnimg.cn/blog_migrate/6b3f80080511b375d25dcbb3e865288d.png)
super
super是指向父类的引用
super可以在子类构造器中,调用父类的某个构造器
如果构造方法没有显示地调用父类的构造方法,那么编译器会自动为它加上一个默认的super()方法调 用。如果父类由没有默认的无参构造方法,编译器就会报错,super()语句必须是构造方法的第一个子 句。
super可以用来区分子父类的同名成员
如果存在同名问题,在子类中调用同名成员,默认有this.成员调用当前子类同名成员,想要调用父类同名成员, 必须定义为super.成员
public class Override02 {
public static void main(String[] args) {
Dog dog = new Dog();
dog.run();//输出的是动物都会跑
}
}
class Anima{//父类
int eye = 2;
public void run(){
System.out.println("动物都会跑");
}
}
class Dog extends Anima{//子类
public void run(){
super.run();//通过super.成员/方法 调用父类中成员/方法
}
}
多态
为什么要使用多态
![](https://i-blog.csdnimg.cn/blog_migrate/f847a41254b97d6f99c55904218a839a.png)
使用传统代码的缺点:代码的复用性不高,而且不利于代码维护
解决方案:使用多态
多态的基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上
多态存在要有三个必要条件:
要有继承
要发生重写
父类引用指向子类对象
多态的具体体现:
方法的多态
重写和重载就体现多态
对象的多态
牢记几点:
一个对象的编译类型和运行类型可以不一致
编译类型在定义对象时,就确定了,不能改变
运行类型是可以变化的
编译类型看定义时 = 号的左边 , 运行类型看 = 号的右边
public class WdPoly {
public static void main(String[] args) {
//父类引用指向子类对象☆☆☆☆☆
//编译类型是Animal 可以指向(接收)Animal子类的对象
Animal animal = new Dog();
//属性调用时,仍然是基类的属性。属性没有多态
System.out.println(animal.age);
animal.shout();//行为(方法)存在多态
}
}
class Animal{
public int age=10;
public void shout(){
System.out.println("叫了一声");
}
}
class Dog extends Animal{
public int age = 28;
public void shout(){
System.out.println("汪汪汪!");
}
public void bone(){
System.out.println("我爱吃骨头和便便");
}
}
向上转型
本质:父类的引用指向了子类的对象
语法:父类类型 引用名 = new 子类类型()
特点:编译类型看左边,运行类型看右边。可以调用父类中的所有成员(属性和方法)(需要遵守访问权限),不能调用子类特有成员;最终运行效果看子类的具体实现。
向下转型
语法:子类类型 引用名 = (子类类型)父类引用‘
只能强转父类的引用,不能强转父类的对象
要求父类的引用必须指向的是当前目标类型的对象
![](https://i-blog.csdnimg.cn/blog_migrate/e30f818e3b9ff788d7aff5004a96e57b.png)
当向下转型后可以调用子类类型中所有的成员
Cat cat = (Cat) animal//向下转型:子类类型 引用名 =(子类类型) 父类引用
属性没有重写之说!属性的值看编译类型
public class Poly03 {
public static void main(String[] args) {
//属性没有重写之说!属性的值看编译类型
Base base = new Sub();//向上转型
System.out.println(base.count);//? 看编译类型 10
Sub sub = new Sub();//20
}
}
class Base { //父类
int count = 10;
}
class Sub extends Base{//子类
int count = 20;
}
![](https://i-blog.csdnimg.cn/blog_migrate/d6a4557b8ef1fbbe4dff1ab9ec019dfa.png)
抽象类(abstract)
当父类的一些方法不能确定时,可以用abstract关键字来修饰该方法,这个方法就是抽象方法,用abstract来修饰该类就是抽象类。
当一个类中存在抽象方法时,需要将该类声明为abstract类
一般来说,抽象类会被继承,有其子类来实现抽象方法
抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类。在框架和设计模式使用较多。
abstract class Animal{
private String name;
public Animal(String name) {
this.name = name;
}
//思考:这里的eat 你实现了,其实没有什么意义
//即: 父类方法不确定性的问题
// =====>考虑将该方法设计为 抽象方法
//=====>所谓抽象方法就是没有实现方法
//=====>所谓没有实现就是指 没有方法体
// public void eat(){
// System.out.println("这是一个动物,但是不知道吃什么");
// }
//当一个类中存在抽象方法时,需要将该类声明为abstract类
//一般来说,抽象类会被继承,有其子类来实现抽象方法
public abstract void eat();
}
抽象类细节:
抽象类不能被实例化(不能有对象,你都抽了它了)
public class abstract02 {
new animal();//报错
}
abstract class animal{
abstract void show();
}
抽象类不一定要包括abstract方法。也就是说,抽象类可以没有abstract方法,但可以有实现方法。
有abstract方法,这个类就一定要声明为abstract类
abstract class animal{
//这是实现的方法
public void show(){
System.out.println("滚犊子");
}
}
abstract 只能修饰类和方法,不能修饰属性和其它的
class C {
public abstract int n1 = 1;//会报错
}
抽象类可以有任意成员【抽象类还是类】,比如说,非抽象(实现)方法、构造器、静态属性等等
class D{
public int n1 = 1;
public static String name = "小甘";
public void hi(){
System.out.println("hi");
}
}
抽象方法不能有主体{ } ,既不能实现
![](https://i-blog.csdnimg.cn/blog_migrate/8172105eb6acf8e43133c64ffeca0739.png)
如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类(有点霸道哦)
abstract class E{
public abstract void hi();
}
abstract class F extends E{
//要么自己也声明成abstract类
}
class G extends E{
public void hi(){//要么实现父类E的抽象方法(发生了重写),所谓实现方法,就是有方法体
}
}
抽象方法不能使用private、final、static关键字修饰,因为这些关键字都是和重写相违背的。
final:被final修饰的类不能有子类,方法就不能重写,但abstract必须要有子类,且必须发生重写
static:被static修饰的方法可以通过类名调用,但abstract必须通过子类实现。
private:被private修饰的方法不能重写,但abstract必须要重写
接口(interface)
为什么要有接口:现实中的例子就是 type-c接口。
中心观点;接口是希望有别的类来实现它的
package HomeWork.interface01;
import org.w3c.dom.ls.LSOutput;
public class interface01 {
public static void main(String[] args) {
//创建手机,相机对象
Phone phone = new Phone();
Camera camera = new Camera();
//创建电脑
Computer computer = new Computer();
computer.work(phone);//把手机接入到电脑
System.out.println("=============");
computer.work(camera);
}
}
interface Usb{
//规定接口的相关方法,自己规定的,
public void start();
public void stop();
}
//Phone 实现 Usb 接口
//解读: 即Phone类需要实现 UsbInterface接口,就是把接口的方法实现了
class Phone implements Usb{
@Override
public void start() {
System.out.println("手机开始工作了....");
}
@Override
public void stop() {
System.out.println("手机停止工作了...");
}
}
//实现接口
class Camera implements Usb{
@Override
public void start() {
System.out.println("相机开始工作了...");
}
@Override
public void stop() {
System.out.println("相机停止工作了...");
}
}
class Computer{
//编写方法来接入接口
public void work(Usb usbInterface){
//通过接口 来调用方法
usbInterface.start();
usbInterface.stop();
}
}
基本介绍:
interface和class是同一级别的人物
接口就是给出一些没有实现的方法(即抽象类方法),封装到一起,到某类要是用的时候,在根据具体情况把这些方法写出来(一般是重写)
语法:implement(实施)
interface 接口名{
//属性
//方法(1.抽象方法 2.默认实现方法 3.静态方法)
//在接口中,抽象方法 ,可以省略abstract关键字
}
class 类名 implement 接口{
自己的属性
自己的方法
必须实现的接口的抽象方法(有点类似抽象类介绍的那样)
}
小结:
在jdk7.0前,接口里的所有方法都没有方法体(8.0就可以有了),即都是抽象对象
jdk8.0后,接口可以有静态方法、默认方法,也就是说接口中可以有方法的具体实现。
注意事项和细节
接口不能被实例化
接口中所有的方法是 public方法,可以不用abstract修饰
![](https://i-blog.csdnimg.cn/blog_migrate/c785bb003e1f23ca47948096772878f4.png)
一个普通类实现接口,就必须将该接口的所有方法都实现(霸道总裁又出现了) alt+enter 快捷键
interface A1{
void say();//第一个
void hi();//第二个
}
class B1 implements A1{
@Override
public void say() {
System.out.println("好霸道");
}
@Override
public void hi() {
System.out.println("我喜欢");
}
}
抽象类实现接口时,可以不用实现接口的方法(抽象类比霸道总裁更霸道)
一个类同时可以实现多个接口
interface G1{}//接口1
interface G2{}//接口2
//一个类可以实现多个接口
class Dog implements G1,G2{}//Dog类可以实现两个接口
接口中的属性,只能是final的,而且是pubilc static final 修饰符。比如:int a = 1;实际是 public static final int a =1;(必须初始化)
接口中属性的访问形式:接口名.属性名
接口不能继承其它的类,但是可以继承多个别的接口,接口和接口之间是继承关系,接口内谈实现
interface G1{}
interface G2{}
//接口可以继承其它接口
interface G3 extends G1,G2{
}
接口的修饰符,只能是public和默认,这点和类的修饰符是一样的
继承和接口的区别
![](https://i-blog.csdnimg.cn/blog_migrate/4a0e3cb06ec013e0ff95b72b72f3fdf3.png)
接口的多态
内部类
基本概念
一个类的内部又完整的嵌套了另一个类结构,被嵌套的类被称为内部类(inner class),嵌套其他类的类称为外部类 (outer class)。是我们类的第五大成员(属性、方法、构造器、代码块、内部类)。内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。
基本语法
![](https://i-blog.csdnimg.cn/blog_migrate/f1a791c447cf952e8f0eda241827fcbc.png)
内部类的分类
定义在外部类局部位置上(比如说方法内):
1、 局部内部类(有类名):局部内部类是定义在外部类的局部位置,比如说方法中,并且有类名。
1.1、可以直接访问外部类的所有成员,包含私有的。
1.2、不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的,但可以使用final修饰,因为局部变量也可以使用final。
1.3、作用域:仅仅在定义它的 方法或代码块中
1.4、局部内部类----访问-----外部类的成员【访问方式:直接访问】
1.5、访问方式:创建对象,在访问(注意:必须在作用域内) 即——>外部类在方法中,可以创建Inner02对象,然后调用方法即可
1.6、外部其他类---不能访问--->局部内部类(因为 局部内部类地位是一个局部变量)
1.7、如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
//演示局部内部类的使用
public class LocalInnerClass {
public static void main(String[] args) {
}
}
class Outer02{//外部类
private int n1 = 100;
private void m2(){} //私有方法
public void m1(){//方法
//1.局部内部类是定义在外部类的局部位置,通常在方法中
//3.不能添加访问修饰符,但是可以使用final修饰
//4.作用域:仅仅在定义它的方法中或代码块中
class Inner02{//局部内部类(本质上也仍然是个类,类该有的它都有)
//2.可以直接访问外部类的所有成员,包括私有的。
public void f1(){
//5.局部内部类可以直接访问外部类的所有成员,包括私有的
System.out.println("n1 = "+n1);
m2();//私有方法同样可以访问。
}
}
//6.外部类在方法中,可以创建Inner02对象,然后调用方法即可
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
记住:
1、局部内部类定义在方法中 / 代码块中
2、作用域在方法体中/代码块中
3、本质仍然是一个 类
2、匿名内部类(没有类名,重点)
定义在外部类的成员位置上:
成员内部类(没用static修饰)
静态内部类(使用static修饰)