目录
1.面向对象和面向过程区别与联系
面向过程思想:
①步骤简单清晰,第一步做什么,第二部做什么。。。。。
②面向对象适合处理一些较为简单的问题
面向对象思想:
①思考解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
②面向对象适合处理复杂的问题
注:对于描述复杂的事物,为了从宏观上把握,从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,任然需要面向过程的思路去处理。
2.什么是面向对象
面向对象编程的本质就是:
以类的方式组织代码,以对象的形式封装数据。
面向对象核心思想:
抽象
三大特征:封装、继承、多态
2.构造器
特点:
必须和类名字相同;
必须没有返回类型,也不能写void;
package com.yves.oop;
public class Teacher {
//创建一个类什么也不写,它也会存在一个方法(无参构造器)
String name;
String age;
String phone;
//显式定义无参构造 (若定义了有参构造器,无参构造器必须显式定义,否则默认的无参构造器无效)
public Teacher(){}
//定义一个有参构造函数 (实例化时 设置初始值)
public Teacher(String name,String age,String phone){
this.name = name;
this.age = age;
this.phone=phone;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", phone='" + phone + '\'' +
'}';
}
}
package com.yves.oop;
public class Application {
public static void main(String[] args) {
Teacher t1 = new Teacher();
System.out.println(t1.toString());
Teacher t2 = new Teacher("zhangsan","21","15725543566");
System.out.println(t2.toString());
}
}
结果:
Teacher{name='null', age='null', phone='null'}
Teacher{name='zhangsan', age='21', phone='15725543566'}
小结:
构造器:
1.方法名和类名相同
2.没有返回值
作用:
1.new 本质是在调用构造方法
2.初始化对象的值
注意点:
1.定义了有参构造之后,如果想使用无参构造,必须显式定义一个无参构造器。
3.内存分析
代码部分:
public class Teacher {
String name;
int age;
public String get(){
return name;
}
public void set(){
this.age = 1;
}
}
public class Application {
public static void main(String[] args) {
Teacher teacher1 = new Teacher();
Teacher teacher2 = new Teacher();
teacher1.name = "zhangsan";
}
}
简单画图理解:(图传不上去^^ )
解释:
程序运行加载application类-->将main()方法入栈-->执行代码new Teacher--->加载Teacher类,并调用默认构造生一个对象名为teacher1的对象(此时对应的将对象名(引用变量名)入栈,并在堆中开辟一块内存存放引用对象)-->赋值通过引用变量名操作堆内存放的数据(在图中未体现出来)--->main()方法出栈即带代表结束.
注:
方法区也是在堆中的,静态方法区:static修饰,和类一起加载。
4.封装
高内聚,低耦合 (属性私有)
封装的意义:
1.提高程序安全性,保护数据
2.隐藏代码的实现细节
3.统一接口
4.提高代码可维护性
什么是封装:
通俗地来说就是利用 private关键字私有化属性 提供public 类型get()set()方法来操作这些私有属性(成员变量),以及对方法的封装。(封装是对一个类的封装 包括属性和方法)
package com.yves.oop;
public class Student {
//private属性私有
private String name;
private int age;
private String phone;
//提供一些public类型的 操作私有属性的方法get和set
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age>150){
System.out.println("输入的年龄可能有误请检查!!!(已设置为默认值0)");
this.age = 0;
}else{
this.age = age;
}
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", phone='" + phone + '\'' +
'}';
}
}
package com.yves.oop;
public class Application {
public static void main(String[] args) {
Student student = new Student();
//name属性为private类型的私有属性,不能使用 对象.变量名 调用,应该使用get()set()方法
// student.name = "zhangsan"
student.setName("zhangsan");
//利用封装可以实现一些功能 而这些功能的细节不展现出来
student.setAge(160);
student.setPhone("11262123355");
System.out.println(student);
}
}
结果:
输入的年龄可能有误请检查!!!(已设置为默认值0)
Student{name='zhangsan', age=0, phone='11262123355'}
5.继承
java中继承使用extends关键字;
java中只有单继承没有多继承!(一个子类只能有一个父类,一个父类可以有多个子类)
继承关系的两个类,一个为子类(继承方 extends前),一个为父类(被继承方);
子类可以继承父类的全部方法(非private修饰的)以及属性(非private修饰的);
被关键字final修饰的类不可被继承
重点:super,方法重写。
1.super
super:调用父类的属性以及方法(private修饰的除外)
注意点:
1.super调用父类的构造方法,必须在构造方法的第一个
2.super必须只能出现在子类的方法或者构造方法中!
public class Pet {
public Pet(){
System.out.println("Pet的无参构造");
}
}
public class Dog extends Pet{
//若无其它有参构造器 无参构造器默认可以不用显示写出来
//这里为了证明无参构造器中有默认的一行代码super();将其显式的定义出来
public Dog(){
//隐藏代码调用了父类的无参构造
//这里将super()注释掉 仍会调用父类无参构造函数;证明无参构造器中有默认的一行代码super();
//super();
//若要显式写出super()则必须在第一行负责会报错
System.out.println("dog的无参构造");
}
}
public class Application {
public static void main(String[] args) {
Dog dog = new Dog();
}
}
结果:
Pet的无参构造
dog的无参构造
3.super与this不能同时调用构造方法
VS this:
this:调用当前对象本身
super:代表父类对象引用
结合实例理解继承:
父类:Pet
public class Pet {
protected String name = "cookie";
public static void say(){
System.out.println("cookie、cookie!!!");
}
public static void say(){
System.out.println("发出叫声");
}
}
子类:Dog
public class Dog extends Pet{
private String name = "来福";
public static void say(){
System.out.println("别打了、别打了!!!");
}
public void test(String name){
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
say();
this.say();
super.say();
}
}
主类:
public class Application {
public static void main(String[] args) {
Dog dog = new Dog();
dog.test("旺财");
}
}
结果:
旺财
来福
cookie
别打了、别打了!!!
别打了、别打了!!!
cookie、cookie!!!
2.重写
1.需要有继承关系,子类重写父类的方法
①方法名必须相同,方法体可以不同
②参数列表必须相同
③抛出的异常:范围可缩小不可放大 Exception(大)-->classNotFoundException(小)
2.非静态:非静态方法才叫重写
静态方法:方法的调用只和左边定义的数据类型有关
非静态方法:方法的调用和new的参数类型有关
3.用@Override注解要重写的方法
4.重写的方法不能是private修饰的(不能是私有的)
例1:静态方法(方法的调用只和左边定义的数据类型有关)
public class B {
public static void test(){
System.out.println("B->father");
}
}
public class A extends B{
public static void test(){
System.out.println("A->son");
}
}
public class ABApplication {
public static void main(String[] args) {
A a = new A();
a.test();
B b = new A();
b.test();
}
}
结果:
A->son
B->father
2.非静态方法(方法的调用和new的参数类型有关)
public class B {
public void test(){
System.out.println("B->father");
}
}
public class A extends B{
@Override
public void test(){
System.out.println("A->son");
}
}
public class ABApplication {
public static void main(String[] args) {
A a = new A();
a.test();
B b = new A();
b.test();
}
}
结果:
A->son
A->son
6.多态
多态注意事项:
1.多态是方法的多态,属性没有多态。
2.有父类和子类关系,才有多态
3.存在条件:继承关系、
方法需要被重写、(不能被重写的方法:static、final、private修饰的方法)
父类的引用指向子类的对象 Animal a = new Tiger();
public class Animal {
public void say(){
System.out.println("动物说话了");
}
}
public class Tiger extends Animal{
@Override
public void say() {
System.out.println("tiger说话了");
}
public void eat(){
System.out.println("tiger eat");
}
}
public class ABApplication {
public static void main(String[] args) {
//一个对象的实际类型是确定的(实际类型:new后面的的类型)
//可以指向的引用类型就不确定了 这就是多态
Tiger tiger = new Tiger();
Animal animal = new Tiger(); //父类的引用指向子类
Object o = new Tiger();
//tiger能调用的方法都是自己的或者是从父类继承的
tiger.say();
//animal 父类型 可以指向子类,但是不能调用子类独有的方法
animal.say();
tiger.eat();
anmal.eat();//这里会报错 原因 :animal 父类型 可以指向子类,但是不能调用子类独有的方法
/*
注:
1.对象能执行哪些方法主要看定义对象左边的类型(和右边关系不大)
2.如果父类和子类都有(子类是重写的父类)则调用的是子类的,看右边
例如这里的say()无论tiger还是animal调用的都是子类的
3.如果子类有父类没有的方法 则以父类为“引用类型”的对象则不可直接调用,看左边
4.如果方法是静态的static 则看左边(可看重写中的详细介绍)
*/
}
}
结果:
tiger说话了
tiger说话了
tiger eat
instanceof 关键字
作用:判断一个对象是什么类型的。+
public class Person {
}
public class Teacher extends Person{
}
public class Student extends Person{
}
public class Application {
public static void main(String[] args) {
Object object = new Student();
System.out.println(object instanceof Student);
System.out.println(object instanceof Person);
System.out.println(object instanceof Object);
System.out.println(object instanceof Teacher);
System.out.println("========================================");
Student student = new Student();
System.out.println(student instanceof Student);
System.out.println(student instanceof Person);
System.out.println(student instanceof Object);
// System.out.println(student instanceof Teacher);
//Student 和 Teacher 类之间没有任何关系 比较会出错
}
}
结果:
true
true
true
false
========================================
true
true
true
7.static关键字
1.static修饰属性(变量)
public class Student {
public static int age; //静态变量 可多个对象共同操作(多线程!)
public String name; //非静态变量
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student();
//适用对象S1给age赋值 用s2获取依然能获取到(age为静态变量)name不可以
s1.age = 10;
s2.name = "zhangsan"
System.out.println(s2.age);
System.out.println(s2.name);
}
}
结果:
10
null
2.static 修饰方法(静态方法)
非静态方法可以直接调用非静态方法和静态方法
静态方法不可以直接调用非静态方法(可以通过new一个对象调用)
静态方法可以直接调用静态方法
静态方法可以直接类名.方法名调用(非静态方法不可以,需要new一个对象调用)
public class Student {
public void read(){
eat(); //非静态方法可以直接调用非静态方法
write();//非静态方法可以直接调用静态方法
System.out.println("read");
}
public static void write(){
say(); //静态方法可以直接调用静态方法
Student student = new Student();
student.read(); //静态方法不可以直接调用非静态方法 需要通过new的对象
System.out.println("write");
}
public void eat(){
}
public static void say(){
System.out.println("say");
}
public static void main(String[] args) {
Student.write(); //静态方法可以直接被 类名.表名 调用
Student student = new Student();
student.read(); //非静态方法 需要通过new的对象调用 (main方法也是static的)
}
}
3.静态代码块
public class Person {
{
//匿名代码块:创建对象时自动创建在构造器之前,创建后不可被调用
System.out.println("匿名代码块");
}
static{
//静态代码块:类一加载就执行 且只在加载类时执行一次
/*
触发类加载的几种情况:
1、调用静态成员时,会加载静态成员真正所在的类及其父类。 通过子类调用父类的静态成员时,只会加载父类而不会加载子类。
2、第一次 new 对象的时候 加载(第二次再 new 同一个类时,不需再加载)。
3、加载子类会先加载父类。(覆盖父类方法时所抛出的异常不能超过父类定义的范围)
注:如果静态属性有 final 修饰时,则不会加载,当成常量使用。
*/
System.out.println("静态代码块");
}
public Person(){
//构造器
System.out.println("构造器");
}
public static void main(String[] args) {
Person p1 = new Person();
System.out.println("===========第二次new=============");
Person p2 = new Person(); //第二次new student类时 类已经被加载不会再执行静态代码块中的代码
}
}
结果:
静态代码块
匿名代码块
构造器
===========第二次new=============
匿名代码块
构造器
8.抽象类
abstract修饰符修饰的,如果修饰方法那么就是抽象方法,如果是类那就是抽象类。
抽象类中可以没有抽象方法(可以有普通方法),但是有抽象方法的类一定是抽象类。
抽象类:不能使用new关键字来创建对象,它是用来让子类继承的。
抽象方法:只有方法的声明,没有方法的实现,它是用来让子类实现的
子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。
9.接口
接口:使用 interface关键字修饰的类;
通过接口可以实现“多继承”(实现多个接口的方式);
只有一个方法的接口叫做函数式接口,可以使用lambda表达式简化;
//接口中的方法默认为 public abstract 可以不写
//接口中的属性默认为 public static final修饰的常量
public interface UserService {
boolean add(); //接口中的方法默认为 public abstract 可以不写
boolean del();
Map<Object,String> query();
boolean update(String name);
}
public interface TimeService {
boolean updateTime();
}
// 通过 实现多个接口的方式实现了“多继承”
//实现的每个接口都需要重写该接口的方法,如果实现多个接口则应重写每个接口里的所有方法
public class UserServiceImpl implements UserService,TimeService{
@Override
public boolean add() {
return false;
}
@Override
public boolean del() {
return false;
}
@Override
public Map<Object, String> query() {
return null;
}
@Override
public boolean update(String name) {
return false;
}
@Override
public boolean updateTime() {
return false;
}
}
作用:
1.约束
2.定义一些方法,让不同的人实现
3.默认的属性修饰词 public static final
4.默认的方法修饰词 public abstract
5.接口不能被实例化(没有构造方法)
6.implements 可以实现一个或多个接口
7.必须要重写接口中的方法
10.内部类
1.成员内部类
package com.yves.oop;
public class Outer {
private int id=999;
private int age = 18;
public void out (){
System.out.println("out");
}
/*
成员内部类:
1.Inner类定义在Outer类的内部,相当于Outer类的成员变量的位置,
2.Inner类可以使用任意访问修饰符,如:public、private、protected等
3.Inner类中定义的方法可以访问Outer类中的数据,不受访问控制符的影响。
4.定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象;
即:内部类 对象名 = 外部类对象.new 内部类( );
如创建Inner的内部类对象:要先创建外部类对象:Outer outer = new outer();
创建内部类:Inner inner = outer.new Inner();
访问Inner中的方法:inner.方法名
*/
public class Inner{
public int age = 3;
public void get(){
//可以获得外部类的私有变量
System.out.println("外部类私有变量id=>"+id);
//如果外部类和内部类具有相同的成员变量或方法,内部类可以直接访问内部类的成员变量或方法,
// 但如果内部类访问外部类的成员变量或者方法时,需要使用this关键字;
// 如下面调用外部变量的例子:
System.out.println("外部类age=>"+Outer.this.age);
System.out.println("内部类age=>"+age);
}
}
public static void main(String[] args) {
Outer outer = new Outer();
//通过外部类来实例化内部类
Outer.Inner inner = outer.new Inner();
inner.get();
}
}
result:
外部类私有变量id=>999
外部类age=>18
内部类age=>3
1.Inner类定义在Outer类的内部,相当于Outer类的成员变量的位置, 2.Inner类可以使用任意访问修饰符,如:public、private、protected等 3.Inner类中定义的方法可以访问Outer类中的数据,不受访问控制符的影响。 4.定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象; 即:内部类 对象名 = 外部类对象.new 内部类( ); 如创建Inner的内部类对象:要先创建外部类对象:Outer outer = new outer(); 创建内部类:Inner inner = outer.new Inner(); 访问Inner中的方法:inner.方法名
5.可以获得外部类的私有变量(既然私有变量都可以其它的就更不在话下了)
6.如果外部类和内部类具有相同的成员变量或方法,内部类可以直接访问内部类的成员变量或方法,但如果内部类访问外部类的成员变量或者方法时,需要使用this关键字;
2.静态内部类
package com.yves.oop;
public class Outer {
private int id=999;
private int age = 18;
static String name = "lisi";
static String emil = "1122@qq.com";
public void out (){
System.out.println("out");
}
//static{
// System.out.println("外部静态代码块");
//}
/*
静态内部类就是用static修饰的内部类,这种内部类的特点是:
1、静态内部类不能直接访问外部类的非静态成员,但,可以通过new 外部类().成员的方式访问;
2、如果外部类的静态成员与内部类的静态成员相同, 可以通过"类名.静态成员"来访问外部类的静态成员;如果不同,可以直接调用外部类的静态成员名。
3、创建静态内部类的对象时,不需要外部类的对象,可以直接创建
*/
/*
静态内部类加载机制:
外部类初次加载,会初始化静态变量、静态代码块、静态方法,但不会加载内部类和静态内部类。
实例化外部类,调用外部类的静态方法、静态变量,则外部类必须先进行加载,但只加载一次。
直接调用静态内部类时,外部类不会加载。
*/
public static class InnerStatic{
static String name = "zhangsan";
public void get(){
//可以获得外部类的私有变量 但是静态内部类不能直接访问外部类的非静态成员,但,可以通过new 外部类().成员的方式访问
System.out.println("外部类私有变量id=>"+new Outer().id);
// 如果外部类的静态成员与内部类的静态成员相同,
// 可以通过"类名.静态成员"来访问外部类的静态成员;
System.out.println("外部静态变量name=>"+Outer.name);
System.out.println("内部静态变量name=>"+name);
// 外部类独有,可以直接调用外部类的静态成员名。
System.out.println("外部静态变量emil(外部类独有)=>"+emil);
}
//static{
// System.out.println("内部类静态代码块");
//}
}
}
package com.yves.oop;
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.InnerStatic innerStatic = new Outer.InnerStatic();
innerStatic.get();
}
}
结果:
外部类私有变量idid=>999
外部静态变量name=>lisi
内部静态变量name=>zhangsan
外部静态变量emil(外部类独有)=>1122@qq.com
静态内部类特点: 1、静态内部类不能直接访问外部类的非静态成员,但,可以通过new 外部类().成员的方式访问; 2、如果外部类的静态成员与内部类的静态成员相同, 可以通过"类名.静态成员"来访问外部类的静态成员;如果不同,可以直接调用外部类的静态成员名。 3、可以获得外部类的私有变量 但是静态内部类不能直接访问外部类的非静态成员,但,可以通过new 外部类().成员的方式访问 4、如果外部类的静态成员与内部类的静态成员相同,可以通过"类名.静态成员"来访问外部类的静态成员;如果不同,可以直接调用外部类的静态成员名
静态内部类加载机制: 1、外部类初次加载,会初始化静态变量、静态代码块、静态方法,但不会加载内部类和静态内部类。 2、实例化外部类,调用外部类的静态方法、静态变量,则外部类必须先进行加载,但只加载一次。 3、直接调用静态内部类时,外部类不会加载。
注:若果想测试内部类加载机制可使用静态代码块来测试,注意:不要将main()写在Outer中。这一点非常重要,因为main()调用后,JVM会首先加载main所在的类,如果把main()放在Outer中,则无论在main()内写任何代码,JVM一定会优先加载Outer的静态块,这样便干扰了"静态内部类和外部类的加载过程"的测试。
参考链接:
1.静态内部类加载机制 测试参考Java基础: 静态内部类和外部类的加载过程_爱喝咖啡的程序员的博客-CSDN博客_静态内部类和外部类
2.关于各种内部类 参考
内部类(成员内部类、静态内部类、方法内部类)_liu771626413的博客-CSDN博客_内部类
3.局部内部类
1、方法内部类就是定义在外部类的方法中,方法内部类只在该方法内可以用
2、由于方法内部类不能在外部类的方法以外的地方使用,因此方法内部类不能使用访问控制符和 static 修饰符
4.匿名内部类
1.本质是类同时还是一个对象
2. 目的简化开发(只调用一次就不再使用,避免重复创建类)
3.可以直接访问外部类的所有成员,包含私有的
4.作用域:仅仅在定义它的方法或代码块中
5.基于接口和抽象类创建匿名内部类必须遵循接口和抽象内部类的一些准则(继承/实现所有的方法,可以仅重写所需要用到的一个或多个方法.)
1.基于接口的匿名内部类:
package com.yves.oop;
public class AnonymousInnerClass {
public static void main(String[] args) {
AnonOuter anonOuter = new AnonOuter();
anonOuter.method();
}
}
class AnonOuter{
private int i = 10;
public void method(){
//基于接口的匿名内部类(匿名内部类的目的是为了简化开发
// )
/*
传统想要使用IA中的接口 创建一个IAImpl类实现接口 创建对象 然后调用
需求:只调用一次,后面不再使用 使用基于接口的匿名内部类
编译类型:左边 一般指对象前的定义
运行类型:右边 一般是指new后面的
匿名内部类:
编译类型:IA
运行类型:匿名内部类
*/
/*
IA Anon = new IA(){
@Override
public void say() {
System.out.println("匿名内部类");
}
其实在创建一个匿名内部类时底层会对接口进行实现 实现类名为 当前外部类名$1(这里为 AnonOuter$1)
JDK底层创建匿名内部类 AnonOuter$1,然后立刻创建了AnonOuter$1实例并把地址映射给 anon
*/
IA Anon = new IA(){
@Override
public void say() {
System.out.println("匿名内部类");
}
@Override
public void add() {
}
};
Anon.say();
}
}
interface IA {
void say();
void add();
}
2.基于类和抽象类实现匿名内部类
package com.yves.oop;
public class AnonymousInnerClass {
public static void main(String[] args) {
AnonOuter anonOuter = new AnonOuter();
anonOuter.method();
}
}
class AnonOuter{
private int i = 10;
public void method(){
//基于类实现匿名内部类
/*
匿名内部类:
编译类型:IA
运行类型:匿名内部类
*/
Father father = new Father(){
@Override
public void fatherTest(){
System.out.println("调用了基于类实现的匿名内部类");
}
};
father.fatherTest();
//基于抽象类实现匿名内部类
AnonPet anonPet = new AnonPet(){
@Override
void dog() {
System.out.println("调用了基于抽象类实现的匿名内部类");
}
@Override
void cat() {
}
};
anonPet.dog();
}
}
class Father{
public Father(){}
public Father(String name){}
public void fatherTest(){
}
public void fatherTest2(){
}
}
abstract class AnonPet{
abstract void dog();
abstract void cat();
}
3.匿名内部类当作实参传递
public class AnonymousInnerClass {
public static void main(String[] args) {
AnonOuter anonOuter = new AnonOuter();
//将匿名内部类当作参数传递
anonOuter.sendAnon(new IA() {
@Override
public void say() {
System.out.println("匿名内部类重写的say");
}
@Override
public void add() {}
});
}
}
class AnonOuter{
private int i = 10;
public void sendAnon(IA ia){
ia.say();
}
}
interface IA {
void say();
void add();
}