面向对象
面向对象:注重找“参与者”,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
类和对象的关系
- 万事万物皆对象
- 对象:具体的事物,具体的实体,具体的实例,模板下具体的产品 类:对对象向上抽取出像的部分,公共的部分,形成类,类是抽象的,是一个模板
- 一般在写代码的时候先写类,然后在根据类创建对应的对象。
创建类
属性(field 成员变量):属性用于定义该类或该类对象包含的数据或者说静态特征。属性作用范围是整个类体。
属性定义格式:
[修饰符] 属性类型 属性名 = [默认值] ;
方法:方法用于定义该类或该类实例的行为特征和功能实现。方法是类和对象行为特征的抽象。
方法定义格式:
[修饰符] 方法返回值类型 方法名(形参列表) {
// n条语句
}
void代表没有返回值;方法的作用:重用代码,封装功能,便于修改
示例
package com.msb;
/**
* @Auther: lianzihao
* 创建类:人类
*/
public class Person {
//名词---》属性---》成员变量---》放在类中方法外(注意:我们只把有需要的内容写到代码里,不相关的东西不要放在代码中)
int age ;//年龄
String name;//姓名
double height;//身高
double weight;//体重
//动词---》方法
//吃饭
public void eat(){
int num = 10;//局部变量:放在方法中
System.out.println("我喜欢吃饭");
}
//睡觉:
public void sleep(String address){
System.out.println("我在"+address+"睡觉");
}
//自我介绍:
public String introduce(){
return "我的名字是:"+name+",我的年龄是:"+age+",我的身高是:"+height+",我的体重是:"+weight;
}
}
创建对象
示例
package com.msb;
/**
* @Auther: lianzihao
*/
public class Test {//测试类
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建一个人类的具体的对象/实例:
//创建一个对象,对象的名字叫:zs
//Person 属于 引用数据类型
//第一次加载类的时候,会进行类的加载,初始化创建对象的时候,对象的属性没有给赋 值,有默认的初始化的值。
Person zs = new Person();
zs.name = "张三";
zs.age = 19;
zs.height = 180.4;
zs.weight = 170.4;
//再创建一个对象:
//再次创建类的时候,就不会进行类的加载了,类的加载只在第一次需要的时候加载一次
Person ls = new Person();
ls.name = "李四";
ls.age = 18;
ls.height = 170.6;
ls.weight = 160.5;
//对属性值进行读取:
System.out.println(zs.name);
System.out.println(ls.age);
//对方法进行操作:
//不同的对象,属性有自己的特有的值,但是方法都是调用类中通用的方法。
//属性:各个对象的属性是独立的,
//方法:各个对象的方法是共享的。
zs.eat();
ls.eat();
zs.sleep("教室");
/*String str = zs.introduce();
System.out.println(str);*/
System.out.println(zs.introduce());
}
}
局部变量与成员变量
它们之间的区别如下:
成员变量 | 局部变量 | |
---|---|---|
代码中位置 | 类中方法外定义的变量 | 方法中定义的变量 代码块中定义的变量 |
作用范围 | 当前类的很多方法 | 当前一个方法(当前代码块) |
默认值 | 有 | 没有 |
是否初始化 | 不需要,不建议初始化,后续使用的时候再赋值即可 | 一定需要,不然直接使用的时候报错 |
内存中位置 | 堆内存 | 栈内存 |
作用时间 | 当前对象从创建到销毁 | 当前方法从开始执行到执行完毕 |
引用数据类型:null
示例
package com.msb;
/**
* @Auther: lianzihao
*/
public class Student {
byte e;
short s;
int c ;//成员变量:在类中方法外
long num2;
float f ;
double d;
char ch;
boolean bo;
String name;
public void study(){
int num = 10 ; //局部变量:在方法中
System.out.println(num);//10
//int num ;重复命名,出错了
{
int a;//局部变量:在代码块中
}
int a;
if(1==3){
int b;
}
System.out.println(c);
}
public void eat(){
System.out.println(c);
}
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
Student s = new Student();
System.out.println(s.c);
System.out.println(s.bo);
System.out.println(s.ch);
System.out.println(s.d);
System.out.println(s.e);
System.out.println(s.f);
System.out.println(s.name);
System.out.println(s.num2);
System.out.println(s.s);
s.d = 10.4;
}
}
构造器
示例
package com.msb2;
/**
* @Auther: lianzihao
*/
public class Person {
//构造器:没有任何参数的构造器我们叫做:空参构造器--》空构造器
public Person(){
/*age = 19;
name = "lili";
height = 169.5;*/
}
//属性:
String name;
int age;
double height;
//方法:
public void eat(){
System.out.println("我喜欢吃饭");
}
}
package com.msb2;
/**
* @Auther: lianzihao
*/
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建一个Person类的具体的对象/实例/实体:
/*
创建对象的过程:
1.第一次遇到Person的时候,进行类的加载(只加载一次)
2.创建对象,为这个对象在堆中开辟空间
3.为对象进行属性的初始化动作
new关键字实际上是在调用一个方法,这个方法叫构造方法(构造器)
调用构造器的时候,如果你的类中没有写构造器,那么系统会默认给你分配一个构造器,只 是我们看不到罢了。
可以自己显式 的将构造器编写出来:
构造器的格式:
[修饰符] 构造器的名字(){
}
构造器和方法的区别:
1.没有方法的返回值类型
2.方法体内部不能有return语句
3.构造器的名字很特殊,必须跟类名一样
构造器的作用:不是为了创建对象,因为在调用构造器之前,这个对象就已经创建好了,并 且属性有默认的初始化的值。
调用构造器的目的是给属性进行赋值操作的。
注意:我们一般不会在空构造器中进行初始化操作,因为那样的话每个对象的属性就一样 了。
实际上,我们只要保证空构造器的存在就可以了,里面的东西不用写
*/
Person p = new Person();
System.out.println(p.age);
System.out.println(p.name);
System.out.println(p.height);
Person p2 = new Person();
System.out.println(p2.age);
System.out.println(p2.name);
System.out.println(p2.height);
}
}
构造器重载
示例
package com.msb3.msb2;
/**
* @Auther: lianzihao
*/
public class Person {
//属性:
String name;
int age;
double height;
//空构造器
public Person(){
}
public Person(String name,int age,double height){
//当形参名字和属性名字重名的时候,会出现就近原则:
//在要表示对象的属性前加上this.来修饰 ,因为this代表的就是你创建的那个对象
this.name = name;
this.age = age;
this.height = height;
}
public Person(String a,int b){
name = a;
age = b;
}
//方法:
public void eat(){
System.out.println("我喜欢吃饭");
}
}
package com.msb3.msb2;
/**
* @Auther: lianzihao
*/
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
/*
1.一般保证空构造器的存在,空构造器中一般不会进行属性的赋值操作
2.一般我们会重载构造器,在重载的构造器中进行属性赋值操作
3.在重载构造器以后,假如空构造器忘写了,系统也不会给你分配默认的空构造器了,那么 你要调用的话就会出错了。
4. 当形参名字和属性名字重名的时候,会出现就近原则:
在要表示对象的属性前加上this.来修饰 ,因为this代表的就是你创建的那个对象
*/
Person p = new Person();
/*p.age = 19;
p.name = "lili";
p.height = 180.4;*/
Person p2 = new Person("lili",19,180.4);
System.out.println(p2.age);
System.out.println(p2.height);
System.out.println(p2.name);
}
}
this
创建对象的过程:
- 在第一次遇到一个类的时候,对这个类要进行加载,只加载一次。
- 创建对象,在堆中开辟空间
- 对对象进行初始化操作,属性赋值都是默认的初始值。
- new关键字调用构造器,执行构造方法,在构造器中对属性重新进行赋值
从上面的效果能够看到:this指代的就是当前对象
this关键字用法:
this修饰属性
**总结:**当属性名字和形参发生重名的时候,或者 属性名字 和局部变量重名的时候,都会发生就近原则,所以如果我要是直接使用变量名字的话就指的是离的近的那个形参或者局部变量,这时候如果我想要表示属性的话,在前面要加上:this.修饰
示例
package com.msb4;
/**
* @Auther: lianzihao
*/
public class Person {
//属性
int age;
String name;
double height;
//空构造器
public Person(){
}
//有参构造器
public Person(int age,String name,double height){
this.age = age;
this.name = name;
this.height = height;
}
//方法:
public void eat(){
int age = 10;
System.out.println(age);//就近原则,age指的是离它近的age--》局部变量的age
System.out.println(this.age);//这里指代的就是属性的age
System.out.println("我喜欢吃饭");
}
}
this修饰方法
**总结:**在同一个类中,方法可以互相调用,this.可以省略不写。
示例
package com.msb4;
/**
* @Auther: lianzihao
*/
public class Person {
//属性
int age;
String name;
double height;
//空构造器
public Person(){
}
//有参构造器
public Person(int age,String name,double height){
this.age = age;
this.name = name;
this.height = height;
}
//方法:
/*public void eat(){
int age = 10;
System.out.println(age);//就近原则,age指的是离它近的age--》局部变量的age
System.out.println(this.age);//这里指代的就是属性的age
System.out.println("我喜欢吃饭");
}*/
public void play(){
/*this.*/eat();
System.out.println("上网");
System.out.println("洗澡");
}
public void eat(){
System.out.println(/*this.*/age);
System.out.println("吃饭");
}
}
this修饰构造器
总结:同一个类中的构造器可以相互用this调用,注意:this修饰构造器必须放在第一行
示例
package com.msb4;
/**
* @Auther: msb-zhaoss
*/
public class Person {
//属性
int age;
String name;
double height;
//空构造器
public Person(){
}
//有参构造器
public Person(int age,String name,double height){
this(age,name);
this.height = height;
}
public Person(int age,String name){
this(age);
this.name = name;
}
public Person(int age){
this.age = age;
}
//方法:
/*public void eat(){
int age = 10;
System.out.println(age);
//就近原则,age指的是离它近的age--》局部变量的age
System.out.println(this.age);//这里指代的就是属性的age
System.out.println("我喜欢吃饭");
}*/
public void play(){
/*this.*/eat();
System.out.println("上网");
System.out.println("洗澡");
}
public void eat(){
System.out.println(/*this.*/age);
System.out.println("吃饭");
}
}
static
static可以修饰:属性,方法,代码块,内部类。
static修饰属性
示例
package com.msb5;
/**
* @Auther: lianzihao
*/
public class Test {
//属性:
int id;
static int sid;
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建一个Test类的具体的对象
Test t1 = new Test();
t1.id = 10;
t1.sid = 10;
Test t2 = new Test();
t2.id = 20;
t2.sid = 20;
Test t3 = new Test();
t3.id = 30;
t3.sid = 30;
//读取属性的值:
System.out.println(t1.id);
System.out.println(t2.id);
System.out.println(t3.id);
System.out.println(t1.sid);
System.out.println(t2.sid);
System.out.println(t3.sid);
}
}
内存分析
官方推荐的访问方式:可以通过类名.属性名的方式去访问
总结:
- 在类加载的时候一起加载入方法区中的静态域中
- 先于对象存在
- 访问方式: 对象名.属性名 类名.属性名(推荐)
static修饰属性的应用场景:某些特定的数据想要在内存中共享,只有一块 --》这个情况下,就可以用static修饰的属性
示例
package com.msb5;
/**
* @Auther: lianzihao
*/
public class MsbStudent {
//属性:
String name;
int age;
static String school;
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
MsbStudent.school = "马士兵教育";
//创建学生对象:
MsbStudent s1 = new MsbStudent();
s1.name = "张三";
s1.age = 19;
//s1.school = "马士兵教育";
MsbStudent s2 = new MsbStudent();
s2.name = "李四";
s2.age = 21;
//s2.school = "马士兵教育";
System.out.println(s2.school);
}
}
属性:静态属性(类变量) 非静态属性(实例变量)
static修饰方法
示例
package com.msb5;
/**
* @Auther: lianzihao
*/
public class Demo {
int id;
static int sid;
public void a(){
System.out.println(id);
System.out.println(sid);
System.out.println("------a");
}
//1.static和public都是修饰符,并列的没有先后顺序,先写谁后写谁都行
static public void b(){
//System.out.println(this.id);//4.在静态方法中不能使用this关键字
//a();//3.在静态方法中不能访问非静态的方法
//System.out.println(id);//2.在静态方法中不能访问非静态的属性
System.out.println(sid);
System.out.println("------b");
}
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//5.非静态的方法可以用对象名.方法名去调用
Demo d = new Demo();
d.a();
//6.静态的方法可以用 对象名.方法名去调用 也可以 用 类名.方法名 (推荐)
Demo.b();
d.b();
}
}
代码块
代码块分类:普通块,构造块,静态块,同步块(多线程)
示例
package com.msb6;
/**
* @Auther: lianzihao
*/
public class Test {
//属性
int a;
static int sa;
//方法
public void a(){
System.out.println("-----a");
{
//普通块限制了局部变量的作用范围
System.out.println("这是普通块");
System.out.println("----000000");
int num = 10;
System.out.println(num);
}
//System.out.println(num);
//if(){}
//while(){}
}
public static void b(){
System.out.println("------b");
}
//构造块
{
System.out.println("------这是构造块");
}
//静态块
static{
System.out.println("-----这是静态块");
//在静态块中只能方法:静态属性,静态方法
System.out.println(sa);
b();
}
//构造器
public Test(){
System.out.println("这是空构造器");
}
public Test(int a){
this.a = a;
}
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
Test t = new Test();
t.a();
Test t2 = new Test();
t2.a();
}
}
执行顺序总结:父类静态代码块 > 子类静态代码块 > 父类代码块 > 父类构造方法 > 子类代码块 > 子类构造方法
三大特性
封装
封装实际上使用方法将类的数据隐藏起来,控制用户对类的修改和访问数据的程度。适当的封装可以让程式码更容易理解和维护,也加强了程式码的安全性。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提
高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露
的暴露出来。这就是封装性的设计思想。
示例
package com.msb.test01;
/**
* @Auther: lianzihao
*/
public class Girl {//女孩
//属性:
private int age;
//读取年龄:
public int duquAge(){
return age;
}
//设置年龄:
public void shezhiAge(int age){
if(age >= 30 ){
this.age = 18;
}else{
this.age = age;
}
}
}
package com.msb.test01;
/**
* @Auther: lianzihao
*/
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建一个Girl类的对象:
Girl g = new Girl();
/*g.age = 33;
System.out.println(g.age);*/
//设置年龄:
g.shezhiAge(31);
//读取年龄:
System.out.println(g.duquAge());
}
}
- 将属性私有化,被private修饰–》加入权限修饰符,一旦加入了权限修饰符,其他人就不可以随意的获取这个属性
- 提供public修饰的方法让别人来访问/使用
- 即使外界可以通过方法来访问属性了,但是也不能随意访问,因为咱们在方法中可以加入 限制条件。
示例
package com.msb.test2;
/**
* @Auther: lianzihao
*/
public class Student {
//属性:
private int age;
private String name;
private String sex;
//加入对应的setter和getter方法:
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
if("男".equals(sex) || "女".equals(sex) ){//sex是男 或者 是 女
this.sex = sex;
}else{
this.sex = "男";
}
}
//加入构造器:
public Student(){
}
public Student(int age,String name,String sex){
this.age = age;
this.name = name;
//this.sex = sex;
this.setSex(sex);
}
}
继承
继承是对类的抽象
总结:继承 就是 is - a 的关系
示例
package com.msb.test03;
/**
* @Auther: lianzihao
*/
public class Person {
//属性:
private int age;
private String name;
private double height;
//提供setter getter方法:
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
//方法:
public void eat(){
System.out.println("可以吃饭。。。");
}
public void sleep(){
System.out.println("可以睡觉。。。");
}
}
package com.msb.test03;
/**
* @Auther: lianzihao
*/
public class Student extends Person {//子类Student 继承 父类Person
//属性:
private int sno;//学号
public int getSno() {
return sno;
}
public void setSno(int sno) {
this.sno = sno;
}
//方法:
public void study(){
System.out.println("学生可以学习");
}
}
package com.msb.test03;
/**
* @Auther: lianzihao
*/
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建子类Student的对象
Student s = new Student();
s.setSno(1001);
s.setAge(18);
s.setName("菲菲");
s.setHeight(180.4);
System.out.println("学生名字为:"+s.getName()+",学生的年 纪:"+s.getAge());
//访问方法:
s.study();
s.eat();
s.sleep();
}
}
提高代码的复用性
继承具有传递性
权限修饰符
private:在当前类中可以访问
default:缺省修饰符,同一个包下的其他类都可以访问
protected:不同包下的子类可以访问
public:在整个项目中都可以访问
总结:属性,方法:修饰符:四种:private,缺省,protected,public
类:修饰符:两种:缺省,public
重写
定义:发生在子类和父类中,当子类对父类提供的方法不满意的时候,要对父类的方法进行重写。
子类的方法名字和父类必须一致,参数列表(个数,类型,顺序)也要和父类一致。
示例
public class Person {
public void eat(){
System.out.println("吃食物");
}
public void sleep(){
System.out.println("睡觉");
}
}
public class Student extends Person {
public void study(){
System.out.println("学习");
}
@override
public void eat(){
System.out.println("我喜欢吃小龙虾喝啤酒。。");
}
}
重载和重写的区别:
重载:在同一个类中,当方法名相同,形参列表不同的时候 多个方法构成了重载
重写:在不同的类中,子类对父类提供的方法不满意的时候,要对父类的方法进行重写。
位置 | 修饰符 | 返回值 | 方法名 | 参数 | 抛出异常 | 方法体 | |
---|---|---|---|---|---|---|---|
重载(overload) | 同一个类中 | 无关 | 无关 | 必须相同 | 必须不同 | 无关 | 不同 |
重写(override) | 子父类中 | 父类的权限修饰符要低于子类 | 父类的返回值类型大于子类 | 必须相同 | 必须相同 | 小于等于 | 不同 |
super
super指的是父类的
super可以修饰属性,可以修饰方法
在子类的方法中,可以通过 super.属性 super.方法 的方式,显示的去调用父类提供的属性,方法。在通常情况下,super.可以省略不写:
==当子类和父类的属性重名时,你要想使用父类的属性,必须加上修饰符super.,==只能通过super.属性来调用
==当子类和父类的方法重名时,你要想使用父类的方法,必须加上修饰符super.,==只能通过super.方法来调用
super修饰构造器
我们平时写的构造器的第一行都有:super() -->作用:调用父类的空构造器,只是我们一般都省略不写(所有构造器的第一行默认情况下都有super(),但是一旦你的构造器中显示的使用super调用了父类构造器,那么这个super()就不会给你默认分配了。如果构造器中没有显示的调用父类构造器的话,那么第一行都有super(),可以省略不写)
如果构造器中已经显示的调用super父类构造器,那么它的第一行就没有默认分配的super();了
在构造器中,super调用父类构造器和this调用子类构造器只能存在一个(它们都需要放在第一行)
多态
多态跟属性无关,多态指的是方法的多态,而不是属性的多态。
定义:多态就是多种状态:同一个行为,不同的子类表现出来不同的形态。同一个方法调用,然后由于对象不同会产生不同的行为。
多态三要素:
- 继承:Cat extends Animal ,Pig extends Animal, Dog extends Animal
- 重写:子类对父类的方法shout()重写
- 父类引用指向子类对象 Animal an = new Pig();
示例
public class Animal {//父类:动物:
public void shout(){
System.out.println("我是小动物,我可以叫。。。");
}
}
public class Cat extends Animal{
//喊叫方法:
public void shout(){
System.out.println("我是小猫,可以喵喵叫");
}
public void scratch(){
System.out.println("我是小猫,我可以挠人");
}
}
public class Dog extends Animal{
//喊叫:
public void shout(){
System.out.println("我是小狗,我可以汪汪叫");
}
public void guard(){
System.out.println("我是小狗,我可以看家护院,保护我的小主人。。。");
}
}
public class Pig extends Animal{
public void shout(){
System.out.println("我是小猪,我嗯嗯嗯的叫");
}
public void eat(){
System.out.println("我是小猪,我爱吃东西。。");
}
}
public class Girl {
//跟猫玩耍:
/*public void play(Cat cat){
cat.shout();
}*/
//跟狗玩耍:
/*public void play(Dog dog){
dog.shout();
}*/
//跟小动物玩耍:
public void play(Animal an){
an.shout();
}
}
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//具体的猫:--》猫的对象
//Cat c = new Cat();
//具体的小女孩:--》女孩的对象
Girl g = new Girl();
//小女孩跟猫玩:
//g.play(c);
//具体的狗---》狗的对象:
//Dog d = new Dog();
//小女孩跟狗玩:
//g.play(d);
//具体的动物:--》动物的对象:
//Cat c = new Cat();
//Dog d = new Dog();
Pig p = new Pig();
Animal an = p;
g.play(an);
}
}
父类当方法的形参,然后传入的是具体的子类的对象,然后调用同一个方法,根据传入的子类的不同展现出来的效果也不同,构成了多态。
public void play(Animal an){//Animal an = an = new Pig();
an.shout();
}
简单工厂设计模式
- 定义一个static方法,通过类名直接调用
- 返回值类型是父类类型,返回的可以是其任意子类类型
- 传入一个字符串类型的参数,工厂根据参数创建对应的子类产品
public class Test {
public static void main(String[] args) {
Girl g = new Girl();
//Cat c = new Cat();
//Dog d = new Dog();
//Pig p = new Pig();
Animal an = PetStore.getAnimal("狗");
g.play(an);
}
}
public class PetStore {//宠物店 ---》工厂类
//方法:提供动物
public static Animal getAnimal(String petName){//多态的应用场合(二)
Animal an = null;
if("猫".equals(petName)){//petName.equals("猫") --》这样写容易发生空指针异常
an = new Cat();
}
if("狗".equals(petName)){
an = new Dog();
}
if("猪".equals(petName)){
an = new Pig();
}
return an;
}
}
final
修饰变量
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//第1种情况:
//final修饰一个变量,变量的值不可以改变,这个变量也变成了一个字符常量,约定俗称 的规定:名字大写
final int A = 10;//final修饰基本数据类型
//A = 20; 报错:不可以修改值
//第2种情况:
final Dog d = new Dog();//final修饰引用数据类型,那么地址值就不可以改变
//d = new Dog(); -->地址值不可以更改
//d对象的属性依然可以改变:
d.age = 10;
d.weight = 13.7;
//第3种情况:
final Dog d2 = new Dog();
a(d2);
//第4种情况:
b(d2);
}
public static void a(Dog d){
d = new Dog();
}
public static void b(final Dog d){//d被final修饰 ,指向不可以改变
//d = new Dog();
}
}
修饰方法
final修饰方法,那么这个方法不可以被该类的子类重写
修饰类
final修饰类,代表没有子类,该类不可以被继承,一旦一个类被final修饰,那么里面的方法也没有必要用final修饰了
抽象类(抽象方法)
抽象类中可以定义0-n个抽象方法
作用:在抽象类中定义抽象方法,目的是为了为子类提供一个通用的模板,子类可以在模板的基础上进行开发,先重写父类的抽象方法,然后可以扩展子类自己的内容。
示例
package com.msb.test03;
//4.一个类中如果有方法是抽象方法,那么这个类也要变成一个抽象类。
//5.一个抽象类中可以有0-n个抽象方法
public abstract class Person {
//1.在一个类中,会有一类方法,子类对这个方法非常满意,无需重写,直接使用
public void eat(){
System.out.println("一顿不吃饿得慌");
}
//2.在一个类中,会有一类方法,子类对这个方法永远不满意,会对这个方法进行重写。
//3.一个方法的方法体去掉,然后被abstract修饰,那么这个方法就变成了一个抽象方法
public abstract void say();
public abstract void sleep();
}
//6.抽象类可以被其他类继承:
//7.一个类继承一个抽象类,那么这个类可以变成抽象类
//8.一般子类不会加abstract修饰,一般会让子类重写父类中的抽象方法
//9.子类继承抽象类,就必须重写全部的抽象方法
//10.子类如果没有重写父类全部的抽象方法,那么子类也可以变成一个抽象类。
class Student extends Person{
@Override
public void say() {
System.out.println("我是东北人,我喜欢说东北话。。");
}
@Override
public void sleep() {
System.out.println("东北人喜欢睡炕。。");
}
}
class Demo{
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//11.创建抽象类的对象:--> 抽象类不可以创建对象
//Person p = new Person();
//12.创建子类对象:
Student s = new Student();
s.sleep();
s.say();
//13.多态的写法:父类引用只想子类对象:
Person p = new Student();
p.say();
p.sleep();
}
}
面试题
-
抽象类不能创建对象,那么抽象类中是否有构造器?
抽象类中一定有构造器。构造器的作用 给子类初始化对象的时候要先super调用父类的构造器。
-
抽象类是否可以被final修饰?
不能被final修饰,因为抽象类设计的初衷就是给子类继承用的。要是被final修饰了这个抽象类了,就不存在继承了,就没有子类。
接口
JDK8之前,接口只有两部分内容:
- 常量:固定修饰符:public static final
- 抽象方法:固定修饰符:public abstract
JDK8之后,新增非抽象方法:
- 被public default修饰的非抽象方法(default修饰符必须要加上,否则出错)
示例
public interface TestInterface {
//常量:
public static final int NUM= 10;
//抽象方法:
public abstract void a();
//public default修饰的非抽象方法:
public default void b(){
System.out.println("-------TestInterface---b()-----");
}
}
class Test implements TestInterface{
public void c(){
//用一下接口中的b方法:
b();//可以
//super.b();不可以
TestInterface.super.b();//可以
}
@Override
public void a() {
System.out.println("重写了a方法");
}
@Override
public void b() {
}
}
静态方法
- static不可以省略不写
- 静态方法不能重写
示例
public interface TestInterface2 {
//常量:
public static final int NUM = 10;
//抽象方法:
public abstract void a();
//public default非抽象方法;
public default void b(){
System.out.println("-----TestInterface2---b");
}
//静态方法:
public static void c(){
System.out.println("TestInterface2中的静态方法");
}
}
class Demo implements TestInterface2{
@Override
public void a() {
System.out.println("重写了a方法");
}
public static void c(){
System.out.println("Demo中的静态方法");
}
}
class A {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
Demo d = new Demo();
d.c();
Demo.c();
TestInterface2.c();
}
}
疑问:为什么要在接口中加入非抽象方法???
如果接口中只能定义抽象方法的话,那么我要是修改接口中的内容,那么对实现类的影响太大了,所有实现类都会受到影响。现在在接口中加入非抽象方法,对实现类没有影响,想调用就去调用即可。
内部类
成员内部类
示例
package com.msb.test07;
/**
* 1.类的组成:属性,方法,构造器,代码块(普通块,静态块,构造块,同步块),内部类
* 2.一个类TestOuter的内部的类SubTest叫内部类, 内部类 :SubTest 外部类: TestOuter
* 3.内部类:成员内部类 (静态的,非静态的) 和 局部内部类(位置:方法内,块内,构造器 内)
* 4.成员内部类:
* 里面属性,方法,构造器等
* 修饰符:private,default,protect,public,final,abstract
*/
public class TestOuter {
//非静态的成员内部类:
public class D{
int age = 20;
String name;
public void method(){
//5.内部类可以访问外部类的内容
/*System.out.println(age);
a();*/
int age = 30;
//8.内部类和外部类属性重名的时候,如何进行调用:
System.out.println(age);//30
System.out.println(this.age);//20
System.out.println(TestOuter.this.age);//10
}
}
//静态成员内部类:
static class E{
public void method(){
//6.静态内部类中只能访问外部类中被static修饰的内容
/*System.out.println(age);
a();*/
}
}
//属性:
int age = 10;
//方法:
public void a(){
System.out.println("这是a方法");
{
System.out.println("这是一个普通块");
class B{
}
}
class A{
}
//7.外部类想要访问内部类的东西,需要创建内部类的对象然后进行调用
D d = new D();
System.out.println(d.name);
d.method();
}
static{
System.out.println("这是静态块");
}
{
System.out.println("这是构造块");
}
//构造器:
public TestOuter(){
class C{
}
}
public TestOuter(int age) {
this.age = age;
}
}
class Demo{
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建外部类的对象:
TestOuter to = new TestOuter();
to.a();
//9.创建内部类的对象:
//静态的成员内部类创建对象:
TestOuter.E e = new TestOuter.E();
//非静态的成员内部类创建对象:
//错误:TestOuter.D d = new TestOuter.D();
TestOuter t = new TestOuter();
TestOuter.D d = t.new D();
}
}
局部内部类
示例
package com.msb.test08;
/**
* @Auther: msb-zhaoss
*/
public class TestOuter {
//1.在局部内部类中访问到的变量必须是被final修饰的
public void method(){
final int num = 10;
class A{
public void a(){
//num = 20;
System.out.println(num);
}
}
}
//2.如果类B在整个项目中只使用一次,那么就没有必要单独创建一个B类,使用内部类就可以了
public Comparable method2(){
class B implements Comparable{
@Override
public int compareTo(Object o) {
return 100;
}
}
return new B();
}
public Comparable method3(){
//3.匿名内部类
return new Comparable(){
@Override
public int compareTo(Object o) {
return 200;
}
};
}
public void teat(){
Comparable com = new Comparable(){
@Override
public int compareTo(Object o) {
return 200;
}
};
System.out.println(com.compareTo("abc"));
}
}
ter();
to.a();
//9.创建内部类的对象:
//静态的成员内部类创建对象:
TestOuter.E e = new TestOuter.E();
//非静态的成员内部类创建对象:
//错误:TestOuter.D d = new TestOuter.D();
TestOuter t = new TestOuter();
TestOuter.D d = t.new D();
}
}
#### 局部内部类
**示例**
```java
package com.msb.test08;
/**
* @Auther: msb-zhaoss
*/
public class TestOuter {
//1.在局部内部类中访问到的变量必须是被final修饰的
public void method(){
final int num = 10;
class A{
public void a(){
//num = 20;
System.out.println(num);
}
}
}
//2.如果类B在整个项目中只使用一次,那么就没有必要单独创建一个B类,使用内部类就可以了
public Comparable method2(){
class B implements Comparable{
@Override
public int compareTo(Object o) {
return 100;
}
}
return new B();
}
public Comparable method3(){
//3.匿名内部类
return new Comparable(){
@Override
public int compareTo(Object o) {
return 200;
}
};
}
public void teat(){
Comparable com = new Comparable(){
@Override
public int compareTo(Object o) {
return 200;
}
};
System.out.println(com.compareTo("abc"));
}
}