面向对象
面向对象编程(Object-Oriented Programming, OOP)
初识面向对象
面向对象编程的本质就是:以类的方式组织代码,以对象的形式组织封装数据。
抽象
从认识论角度考虑是现有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象
从代码运行角度考虑是先有类后有对象。类是对象的模版
三大特性:
封装
继承
多态
方法回顾和加深
修饰符
返回值类型
可以为八大基本类型和引用类型,也可以为void(无返回值),返回值类型为void时,书写return;
return 与 break区别:
break:跳出switch,结束循环
return:结束方法
参数列表:参数类型+参数名 以及可变长参数
抛出异常:
public void readFile(String file) throws IOException {
}
方法的调用
主要分为有static修饰和无static修饰
当有static修饰符修饰的方法,是跟类一起加载的,即使类没有被实例化也可以直接调用。类名.方法名 调用该方法
当无static修饰符修饰的方法,是类实例话后才加载的,不可以直接调用,需要实例化类后通过类的实例化对象进行调用
//学生类 public void say(){ System.out.println("学生说话了"); } //另一个demo类 public static void main(String[] args) { //对象类型 对象名 = 对象值 student student = new student(); student.say(); }
值传递和引用传递
先来看值传递
//值传递
public class Demo04 {
public static void main(String[] args) {
int a = 1;
int b = 2;
System.out.println(a);
System.out.println(b);
System.out.println("===================change===================");
Demo04.change1(a);
System.out.println(a); //1,因为没有把change方法中的值返回出来,所以并没有发生变化
b = Demo04.change2(b);
System.out.println(b);
}
//返回值为空
public static void change1(int a){
a = 10;
}
public static int change2(int b){
b = 20;
return b;
}
引用传递
//引用传递:一般是传递一个对象,本质还是值传递
//一个class里只能有一个public class,但可以有多个class
public class Demo05 {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name); //null
//这里不同于值传递,因为在change方法中传入的是person这个对象,修改的是person这个对象的.name属性,所以成功了,
Demo05.change(person);
System.out.println(person.name);
}
public static void change(Person person){
person.name = "Zh1z3ven";
}
}
//定义了一个person类,有一个属性:name
class Person{
String name; //null
}
this关键字
代表当前这个类或者对象
对象的创建分析
使用new关键字创建对象
在使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。
类中的构造器也称为构造方法,是在进行创建对象的时候必须调用的。并且构造器有以下两个特点
1、必须和类的名字相同
2、必须没有返回类型,也不能写void
public class Student {
//属性:字段
String name; //创建实例化对象时默认为null
int age; //0
//方法
public void study(){
System.out.println(this.name + "is Learning...");
}
}
public class Application {
public static void main(String[] args) {
//类是抽象的,需要实例化
//类实例化后会返回一个自己的对象
//stduent就是student类的具体实例
Student xiaoming = new Student();
Student agou = new Student();
xiaoming.name = "小明";
xiaoming.age = 18;
System.out.println(xiaoming.name);
System.out.println(xiaoming.age);
agou.name = "阿狗";
agou.age = 18;
System.out.println(agou.name);
System.out.println(agou.age);
}
}
构造方法
1、和类名相同
2、没有返回值
3、new的本质是在调用构造方法
4、初始化对象的值
5、定义有参构造之后,如果想使用无参构造,需要显示的定义一个无参的构造,不然就只有有参构造方法
6、alt + insert 快捷键
public class Person {
//一个类即使什么都写,他也会存在一个构造方法
//构造方法用来初始化值
/* 形如":
public Person(){
}
*/
//显示定义构造方法
String name;
//使用new关键字必须有构造方法,本质是在调用构造方法
//无参构造方法
public Person(){
//可以帮助我们初始化对象的属性值
this.name = "Zh1z3ven";
}
//有参数构造:一旦定义了有参构造,无参构造方法必须显示定义
//this.name代表Person类的属性name
//后面的name为有参构造方法传递的String name参数
//alt+insert生成构造方法
public Person(String name){
this.name = name;
}
public static void main(String[] args) {
//new 实例化了一个对象
Person person = new Person();
System.out.println(person.name);
}
}
创建对象内存分析
代码:
public class Pet {
public String name;
public int age;
//无参构造
public void shout(){
System.out.println("叫了一声");
}
}
public class Application {
public static void main(String[] args) {
Pet dog = new Pet();
dog.name = "旺财";
dog.age = 3;
dog.shout();
System.out.println(dog.name);
System.out.println(dog.age);
}
}
首先会现在方法区加载Application类(类中包括其main()方法,常量池(存放字符串;但int型数字不算在常量池内)),并在栈中加载main方法
main方法在栈的最低下,方法的调用执行都在栈中执行。之后运行到Pet dog = new Pet()
时,先会在方法区实例化对象所需的模版,即加载一个Pet类,包括该类的属性、常量池和非静态方法。之后当加载完后 讲赋值语句右边的new Pet()
赋值给左边的Pet dog
时,在栈中会加载一个dog
的变量,dog
变量暂时只是一个引用变量,引用在堆中生成的实例化对象,类似于指针,该引用指向该实例化对象在堆中的内存地址。
而在实例化对象内的shout()
是在引用方法区中Pet
类中的shout()
方法。之后执行到赋值语句时也就是
dog.name = "旺财";dog.age = 3;
这时会把Application
类中的常量池中的旺财
赋值给堆
中的name
,之后3
赋值给堆中的age
,同时shout()
调用的是Pet
类中的shout()
方法(因为并没有参数传递,所以是同一个方法)
在方法区中的静态方法区内,这里存放的是被static
修饰符修饰的方法都会同类同时被加载,所以可以不用通过实例化对象就可以直接调用被static
修饰的方法。
简单小结类与对象
类是一个模版,是实例化对象的模版
对象是一个具体的实例,是一个类的实例化对象
方法的定义
修饰符 返回值类型 方法名(参数类型 参数名){
方法体
...
return 返回值;
}
方法的调用
调用方法:对象名.方法名(实参列表)
Java支持两种调用方法的方式,根据方法是否有返回值来选择。
return不仅可以返回值,同样可以结束这个方法。例:return 0;结束当前方法。
当方法返回一个值的时候,方法调用通常被当作一个值赋值给一个变量。
int larger = max(30, 40);
当方法返回值为void时,方法调用一定是一条语句
System.out.println("Hello, Zh1z3ven!");
对象的引用
引用类型:
基本类型8个(byte、short、int、long、float、double、boolean、char)
剩下的都是引用类型,对象也是通过引用来操作的:栈-->堆(地址)
属性:字段field 成员变量,默认初始化(数字:0,char:u000,boolean:false,引用:null)
属性的定义:
修饰符 属性类型 属性名 = 属性值
对象的创建和使用
必须使用new关键字创建对象 ,注意构造方法,Person zh1z3ven = new Person()
对象的属性: zh1z3ven.name
对象的方法: zh1z3ven.sleep()
类
静态的属性:属性
动态的行为:方法
面向对象三大特性
封装
程序设计追求“高内聚、低耦合”,高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
属性私有 get/set
属性私有,即用private修饰属性
get/set,对外提供get set方法使得外部可通过该方法控制属性值
同时可在set方法中进行安全设置,提高程序的安全性
/*1、提高程序的安全性2、隐藏代码的实现细节3、统一接口 get、set形成规范4、提高系统可维护性 */public class Student { //属性私有,加private关键字去修饰属性 private String name; private int id; private char sex; private int age; //提供一些可以操作这个属性的方法 //提供public的get或set方法 public String getName(){ return this.name; } public void setName(String name){ this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { if (age > 120 || age < 0){ this.age = 3; }else { this.age = age; } }}public class Application { public static void main(String[] args) { Student s1 = new Student(); s1.setName("Zh1z3ven"); System.out.println(s1.getName()); //并不能通过形似s1.name直接访问操作类的属性,但是可以通过某些可被调用的外部方法去操作这个类的私有属性值 //封装:1、属性私有。2、提供可被外部调用的方法去操作私有属性 //alt+insert 选中getter and setter一键生成 get set方法, //mac为control+return,选中所需要设置的属性即可直接生成 //或者右键选择generate }}
继承
extends
继承的本质是对某一批类的抽象,从而实现对现实世界的更好的建模
Java中只有单继承没有多继承
继承就是扩展的意思,子类是对父类的扩展。
子类可以继承父类的所有方法
继承是类和类之间的一种关系,除此之外还有依赖,组合,聚合等
继承关系的俩个类,一个为子类(派生类)一个为父类(基类)。子类继承父类,使用关键字extends表示
子类和父类之间,从意义上讲因该具有 is a 的关系
Ctrl + h 可以看该类的继承关系
object类
在Java中所有的类都默认直接或间接继承于Object
super
如果父类重写了有参构造,最好也写上无参构造,以便后续子类去继承该父类(不然只能调用父类的有参构造方法,默认是调用无参的构造方法)
- super调用父类的构造方法必须在构造方法的第一个
- super必须只能出现在子类的方法或构造方法中
- super和this不能同时调用构造方法
this代表本身调用者的这个对象
super代表父类对象的引用
super必须存在继承关系才可以使用
this(); 代表本类的构造方法
super(); 代表父类的构造方法
//父类public class Person { public Person(){ System.out.println("Person无参执行了"); } public int money = 1000000000; public void say(){ System.out.println("说了一句话"); } protected String name = "Zh1z3ven"; public void print(){ System.out.println("Person"); }}
//子类public class Student extends Person { //默认调用了父类的无参构造,调用父类的构造方法必须在子类构造方法的第一行 public Student(){ super(); //也可以不写 System.out.println("Student无参执行了"); } private String name = "zh1z3ven"; public void print(){ System.out.println("Student"); } public void test1(){ print(); this.print(); super.print(); } public void test(String name){ System.out.println(name);//传递的参数name System.out.println(this.name);//student的属性name System.out.println(super.name);//父类的属性name }}
public class Application { public static void main(String[] args) { Student student = new Student(); student.test("zz"); student.test1(); }}
方法重写
重写都是非静态方法的重写(static方法属于类的方法,不属于实例的方法,重写指的是重写实例的方法)
final修饰的是常量池中的常量,不能被重写。private方法不能被重写。
子类重写方法的权限修饰符要大于父类 public>protected>default>private
方法名必须相同、参数列表必须相同(不然就是重载了)
抛出异常的范围与权限修饰符的范围一致
子类的方法与父类的方法必须要一致,但是方法体不同
为什么需要重写?
1.父类的功能,子类不一定需要,或者不一定满足
- alt+insert / control + return
@override
@Override //注解:有功能的注释 public void test() { super.test(); //默认调用父类的方法,也可以自己重写 }
当重写一个非静态父类方法时,在子类中contorl + return即可生成一个重写方法
静态方法是属于类的方法,当下面
test()
方法为static
修饰的静态方法时,那么会各自调用自己类中的test()
方法。因为此时调用的是类中的静态方法;而当没有static
修饰时,这时调用的都是A
类中的test()
方法,因为这时调用的是A
的实例化对象加载的test()
方法(只有static
修饰的方法才会和类一起在方法区中同时加载)可以简单理解为一旦子类重写了父类的方法,就执行子类的方法
class A extends BA a = new A();a.test();//父类引用指向子类B b = new A();b.test()A类中test()实现:System.out.println("A=>test()");B类中test()实现:System.out.println("B=>test()");
多态
class Student extends PersonStudent s1 = new Student();Person s2 = new Student();Object s3 = new Student();
一个对象的实际类型是确定的(
new Student();
),但是可以指向的引用类型就不确定了(Student s1
/Person s2
/Object s3
)多态注意事项:
- 多态是方法的多态,属性没有多态
- 父类和子类有联系.
ClassCastException
类型转换异常- 多态存在的条件:继承关系、方法需要重写、父类的引用指向子类对象
Father f1 = new son();
Instanceof
判断一个对象是什么类型,可以判断两个类之间是否存在继承关系
//Object > String //Object > Person > Student //Object > Person > Teacher Object object = new Student(); //Object类型 System.out.println(object instanceof Student); //true System.out.println(object instanceof Person); //true System.out.println(object instanceof Object); //true System.out.println(object instanceof Teacher); //false System.out.println(object instanceof String); //false System.out.println("==================================="); Person person = new Student(); System.out.println(person instanceof Object); //true System.out.println(person instanceof Person); //true System.out.println(person instanceof Student); //true System.out.println(person instanceof Teacher); //false //System.out.println(person instanceof String); //编译报错 }
类型转换
public static void main(String[] args) { //类型之间的转化: 父 子 //低 --> 高 可以自动转换 //高 --> 低 需要强制转换 Person obj = new Student(); //student将这个对象转换为Student类型,就可以使用Studen类型的方法 Student student = (Student) obj; //强制转换 student.go(); ((Student) obj).go(); //强制转换 Person person = student; //自动转化 }
static关键字
static : 修饰成员变量或者方法,修饰为静态变量或者静态方法
被static修饰的属于类,非static的属于对象
静态变量属于类的变量,存储在方法区的静态方法区内,是一块固定的内存,随着类的加载同时加载,当直接使用类去调用一个变量说明此变量就是静态变量
非静态方法也可以直接调用静态方法
静态方法可以直接调用,无需实例化对象
//static : 修饰成员变量或者方法,修饰为静态变量或者静态方法//被static修饰的属于类,非static的属于对象public class Student { //静态变量属于类的变量,存储在方法区的静态方法区内,是一块固定的内存,随着类的加载同时加载,当直接使用类去调用一个变量说明此变量就是静态变量 private static int age; //静态变量 private double score; //非静态变量 public void run(){ go(); //非静态方法也可以直接调用静态方法 } public static void go(){ } public static void main(String[] args) { Student s1 = new Student(); System.out.println(Student.age); System.out.println(s1.age); System.out.println(s1.score); s1.run(); Student.go(); //静态方法可以直接调用,无需实例化对象 go(); }}
public static void main(String[] args) { Student student = new Student(); System.out.println("=================="); Student student1 = new Student(); } // 2 :赋初始值 { System.out.println("匿名代码块"); //匿名代码块 //随着对象的创建而创建,在构造方法之前 } // 1 : 只执行一次,后面不会执行 static { //静态代码快,方便加载初始化一些数据 //随着类的加载而加载,永久只执行一次 System.out.println("静态代码快"); } // 3 //alt+inset/contorl+return --> select none public Student() { System.out.println("构造方法"); }}输出结果:静态代码快匿名代码块构造方法==================匿名代码块构造方法
利用static静态导入包及常量
//静态导入包import static java.lang.Math.random;import static java.lang.Math.PI;
抽象类和接口
抽象类
abstract
修饰符可以修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,该类就是抽象类
抽象类中可以没有抽象方法,但是抽象方法的类一定要声明为抽象类
抽象类,不能使用new关键字来创建对象,它是用来让子类继承的(类是单继承)
抽象方法,只有方法的声明,没有方法的实现,它是用来让子类方法去实现的
子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
public abstract class Action { //抽象方法,只有方法名字,没有方法的实现 public abstract void doSomething(); //抽象类不能new,只能靠子类去实现 //抽象类可以写普通方法 //抽象类存在构造方法吗? //抽象类存在的意义?提高开发的可扩展性}//继承抽象类的子类必须重写抽象方法并实现,除非子类也是abstractpublic class A extends Action{ @Override public void doSomething() { }}
接口
声明接口的关键字是interface
接口可以多继承
普通类:只有具体的实现
抽象类:具体实现和规范(抽象方法)都有
接口:只有规范。约束和实现分离(面向接口编程)
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是。。。你必须能。。”的思想。如果你是天使你必须能飞,如果你是汽车,你必须能跑。
接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
OO的精髓,是对对象的抽象,最能体现这一点的就是接口。
//interface 定义的关键字public interface UserService { //接口中的所有定义其实都是抽象的,不能在接口中实现方法 //修饰符默认为 public abstract //常量。默认修饰符为 public static final int AGE = 99; void add(String name); void delete(String name); void update(String name); void select(String name);}
//实现接口需要重写里面的方法,类可以实现接口//public class 类名(一般以Impl结尾) implements 接口名//接口可以多继承public class UserServiceImpl implements UserService, TimeService{ @Override public void add(String name) { } @Override public void delete(String name) { } @Override public void update(String name) { } @Override public void select(String name) { } @Override public void timer() { }}
public interface TimeService { void timer();}
接口小结
- 接口是一种规范、约束
- 只有方法的定义,没有方法的实现
- 方法默认修饰符为
public abstract
- 常量默认修饰符为
public static final
- 接口不能被实例话,接口没有构造方法
implements
可以实现多个接口- 继承接口的类必须重写实现接口的方法
内部类
内部类主要分为四种:成员内部类、局部内部类、静态内部类、匿名内部类
内部类就是在一个类的内部定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。
成员内部类
public class Outer { private int id = 10; public void out(){ System.out.println("这是外部类的方法"); } public class Inner{ public void in(){ System.out.println("这是内部类的方法"); } //内部类访问外部私有变量 public void getID(){ System.out.println(id); } }}
public static void main(String[] args) { Outer outer = new Outer(); outer.out(); //通过外部类实例话内部类:成员内部类 Outer.Inner inner = outer.new Inner(); inner.in(); inner.getID(); }
静态内部类
静态内部类无法直接访问外部类的非静态属性
public class Outer { private int id = 10; public void out(){ System.out.println("这是外部类的方法"); } public static class Inner{ public void in(){ System.out.println("这是内部类的方法"); } //内部类访问外部私有变量 public void getID(){ System.out.println(id); } }}
还可以在java文件中声明另一个类
public class Outer { private int id = 10; public void out(){ System.out.println("这是外部类的方法"); } public class Inner{ public void in(){ System.out.println("这是内部类的方法"); } //内部类访问外部私有变量 public void getID(){ System.out.println(id); } }}//一个java文件里面只能有一个public class,但是可以有多个classclass A{ }
局部内部类
在类的方法中创建一个类
public class Outer { public void method(){ //局部内部类 class Inner{ } }}
匿名内部类
没有类名的内部类
public class Test { public static void main(String[] args) { //没有名字初始化类 //匿名对象的使用,不用把实例保存在变量中 new Apple().eat(); UserService userService = new UserService(){ @Override public void hello() { } }; } }class Apple{ public void eat(){ System.out.println("1"); }}interface UserService{ void hello();}