目录
什么是面向对象编程?
面向对象编程(Object Oriented Programming,简称OOP)其实就是将所有的程序一个一个地分成子程序,这些子程序有各自的作用,然后将其组合起来,在主函数里调用这些子程序,举个例子,人将大象关进冰箱需要几步?三步。1.人打开冰箱。2.人将大象放进去。3.人关闭冰箱。因此我们可以将这件事拆分成3个对象:人、大象、冰箱。人的方法可以是:1.打开。2.放入。3.关闭。大象的方法可以是:1.进入。冰箱的方法可以是:1.接受(大象)。
这样大家可以看出来什么是面向对象编程了吧,他的优点是:可以隐藏一些不需要的细节,提高程序的复用性,降低程序之间的耦合度,并且安全性得到了保障。
如何定义一个类呢?这里有个公式是: 权限修饰符 class 类名{} 。在一个文件中可以定义多个类,但是只能有一个public类。(潜规则:一个文件一个类)并且public修饰的类必须与文件名同名。
类的成员
类的成员分为两种,一种是属性,一种是方法。当然,这些成员还能再细分成各种分支。
成员之一:属性。
属性=成员变量。属性存储在堆内存中 局部变量存储在栈内存中,成员变量(属性)都有默认值 :byte short int long是0,float double是0.0,char是空格,boolean是false,引用数据类型是null。
公式:权限修饰符 数据类型 变量名 [= 值];
public 公共的,private 私有的(只能类的内部使用)
成员之二:方法(动态)
有返回值公式:权限修饰符 返回值类型 方法名(){语句.... return 返回值;}
无返回方法公式:权限修饰符 void 方法名(){语句.... [return;]}
return 方法的结束 可以return 常量 return 变量 return 表达式 返回值类型如果是引用数据类型,那么return时返回的是这个类型的地址 方法内不能再定义方法,方法之间可以相互调用
创建和使用:类名 对象名 = new 类名(); 对象.属性 对象.方法
设计方法的参数叫形参 使用方法的参数叫实参
匿名对象只能使用一次,要么就当做参数传递例如:
new Person().name = "abc"
System.out.println(new Person().name)
输出:null(引用数据类型的默认值)
如果说程序占用内存较高,那可以使对象置空,即变量 = null
方法链式调用:对象.方法.方法 相当于方法中调用方法,后面的方法取决于前面方法的返回值,有返回值才能调用。
方法的重载(Overload)
在同一个类中,存在同名方法,参数列表不同就是重载(一定要是同名的)。
重载与变量名和返回值无关,与数据类型有关
Java里方法的参数传递只有一种:值传递。基本数据类型:将实际参数的副本传入方法中,参数本身不受影响。引用数据类型:将对象的地址传入或传出方法。例如:
public class TestTransfer {
public static void swap(int a , int b){
int tmp = a;
a = b;
b = tmp;
System.out.println("swap方法里,a的值是" + a + ";b的值是" + b);
}
public static void main(String[] args) {
int a = 6;
int b = 9;
swap(a , b);
System.out.println("交换结束后,变量a的值是" + a + ";变量b的值是" + b);
}
}
swap方法里,a的值是9;b的值是6 交换结束后,变量a的值是6;变量b的值是9
在方法中new一个新的对象,新的对象的值与主函数中的值不相干
重载属于多态的一种体现
如果想要传很多个数,参数中可以用(int ... a),这个相当于一个数组,可以直接传参数,没必要传数组进去了,不过只能有一个多参,并且多参必须放在参数列表的最后面。
构造器(构造方法)
一个类中,如果没有显示定义构造器,那么系统会默认提供一个无参构造器(编译时检查,如果没有就提供)
一旦显示定义了构造器,那么系统默认的无参就没有了,所以要重新自己设置一个无参的构造器
用来初始化,不能有返回值(return)
公式:权限修饰符 类名(参数列表...){初始化语句...}
构造器也可以进行重载,也可以调用普通方法
面向对象三大特征:封装,继承,多态
1.封装
封装的作用:限制对属性的不合理操作(属性私有化),提供公共的getxxx和setxxx方法,便于修改,增强代码的可维护性,隐藏一个类中不需要对外公布的细节
潜规则:属性私有,公共的get set方法(是为属性服务的),再设计其他的方法
方法也可以封装,例如:
//玩play:参数String host,返回值 String “飞盘”, 打印:小狗与主人host玩的很开心
public String play(String host){
System.out.println("小狗与主人"+host+"玩的很开心");
bark();
return "飞盘";
}
//叫bark:打印:小狗喜欢汪汪叫,封装此方法,玩play()的时候才bark()
private void bark(){
System.out.println("小狗喜欢汪汪叫");
}
其实就是将bark方法私有化,只能通过类中的方法来调用。
this关键字
this代表当前类的对象 对象.属性 对象.方法
在构造器和普通方法中都可以用this
this()只能出现在构造器中,且必须出现在构造器的首行,作用是调用本类中的无参构造器,如果想要调其他有参构造器,那可以在括号里加(数据类型 变量)。 当形参与成员变量重名时,如果在方法内部需要使用成员变量,必须添加this来表明该变量时类成员
2.继承(Inherit)
子类继承了父类中的属性和方法(私有的除外),是为了让子类更加关注子类独有的业务
extends 扩展 子类扩展了父类(超类,基类)
一个子类只能有一个父类(java中叫单继承),反过来说,一个父类可以有多个子类,子类和子类之间没有任何关系,子类可以继续有子类(层次无限制,保证单继承就行了)
继承的出现让类与类之间产生了关系,提供了多态的前提
类和类之间继承关系:1.子类 extends 父类 2.子类构造器在调用父类构造器(默认情况下,无参调无参,有参调有参)
当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器,且必须放在构造器的第一行
如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错
方法的重写(override)
父类方法已经无法满足子类需要,就可以对父类的方法进行升级改造。
重写:返回值类型 方法名 参数列表必须相同,子类的权限修饰符必须要大于等于父类
重写后,此时父类方法已经不可见,调用的都是子类中的方法,重写和被重写的方法须同时为static的,或同时为非static的子类方法抛出的异常不能大于父类被重写方法的异常
super关键字
在Java类中使用super来调用父类中的指定操作
super没有类对象的概念 向上(父类)找
super.父类(不限于直接父类)的属性和方法
super() 相当于使用父类的构造器。必须出现在构造器首行
super()和this()只能出现一个
如果父类有有参构造器则子类中有参构造器的首行如果为super(),则自动调用父类中的构造器内容
访问权限:
同一个包中,下面三个修饰的无论是在类内部还是类外部都可以用
修饰符 | 类内部 | 同一个包 | 子类 | 任何地方 |
private | Yes | |||
default | Yes | Yes | ||
protected | Yes | Yes | Yes | |
public | Yes | Yes | Yes | Yes |
重写代码举例:
class Parent {
public void method1() {
System.out.println("父类方法")
}
}
class Child extends Parent {
private void method1() {
System.out.println("子类方法")
}
//非法,子类中的method1()的访问权限private比被覆盖方法的访问权限public弱
}
public class UseBoth {
public static void main(String[] args) {
Parent p1 = new Parent();
Child p2 = new Child();
p1.method1();
p2.method1();
}
}
输出:异常(因为子类中的method1()的访问权限private比被覆盖方法的访问权限public弱)
若正确(private改成public),则输出:父类方法 子类方法
3.多态(Polymorphism)
多态是建立在继承的基础上的
属性不具有多态性,只有方法具有多态性
基本上都是定义父类然后传子类
公式:父类 对象名 = new 子类构造器();
对象的多态性,都是用于设计参数,设计参数时规定了传入参数的一个范围
等号左边:编译时类型(写代码的过程)
等号右边:运行时类型(运行代码实际产生的对象)
方法的调用是在运行时确定的
运行时调用子类中重写的方法,写代码时调用父类的方法
当编译时类型与运行时类型不一致时,就发生多态(左边为父类,右边为子类)
举例:
public class Animal {
public void eat(){
System.out.println("吃饭");
}
}
public class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头");
}
public void sleep(){
System.out.println("狗睡觉");
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Dog();
a.eat();
((Dog) a).sleep();
}
}
如果想要调用子类的方法,那就需要对父类进行强制类型转换(向下转型)
基本数据类型有大小之分(byte 2 字节,int 4 字节等),引用数据类型也有大小之分(父类大,子类小)
多态属于自动转换类型(向上转型)
关键字 instanceof(a instanceof B)
主要用于判断是否为右边的类型,并且返回boolean类型的数据,只有判断了才能向下转型。
public class Simple {
public static void main(String[] args) {
Simple simple = new Simple();
System.out.println(simple instanceof Simple);
}
}
输出为True
要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
如果a属于类B的子类A,a instanceof B值也为true。
引用类型转换分为强制转换和自动转换。自动转换中小的数据类型可以自动转换成大的数据类型,强制转换可以把大的数据类型强制转换(casting)成小的数据类型,但是需要使用instanceof
无继承关系的引用类型间的转换是非法的
在造型前可以使用instanceof操作符测试一个对象的类型
Object是所有引用数据类型的父类
Object类中的主要方法:
No, | 方法名称 | 类型 | 描述 |
1 | public Object() | 构造 | 构造方法 |
2 | public boolean equals(Object obj) | 普通 | 对象比较 |
3 | public int hashCode() | 普通 | 取得Hash码 |
4 | public String toString() | 普通 | 对象打印时调用 |
==(比较的是堆内存中的值) 比较基本数据类型是比较值,比较引用数据类型是比较地址
equals() 只能比较引用数据类型,比较是否指向同一个对象。
格式:obj1.equals(obj2)
特例:当用equals()方法进行比较时,对类File、String、Date及包装类(Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个对象
原因:在这些类中重写了Object类的equals()方法
堆内存中有一个字符串常量池,字符串都放在字符串常量池中,如果说里面已经有一个相同的地址,则不会再产生一个,因此两个都指向同一个地址
toString()方法
在输出引用数据类型的数据时,自动调用toString()方法
Date now=new Date();
System.out.println(“now=”+now); 相当于
System.out.println(“now=”+now.toString());
返回值:类名+引用地址。
可以根据需要重写toString方法
包装类
除了int变成Integer、char变成Character,其他的都是将首字母大写形成包装类
将基本数据类型赋值给引用数据类型称为装箱
反之则为拆箱(仅限于基本数据类型)
在Integer常量池的范围是-128-127,如果不在这个常量池中,则相当于自动new一个新的,在这个范围中,他们的内存地址是一样的,因此比较两值时可以用==,如果超出这个范围,则用equals
没有小数常量池,因此Double,float没有常量池
(equalsIgnreCase(String anotherString)比较两个String类型数据,若长度相同且各个字符串相同,则返回真,反之返回假(lang包下))
static(静态的)
静态的属性被所有对象共享,内存空间中有一个静态域,其中的对象都指向静态域。
静态属性优先于对象的存在,随着类的加载优先加载到内存中
公式:类名.静态的属性(权限允许情况下)
static也可以修饰方法,也是放在静态域中,静态方法 类名.方法
静态方法不能调用普通的属性和方法,只能new一个对象才能调用属性和方法,或者可以调用静态的方法(看堆内存中的静态域中是否有对应的属性或方法)
静态方法中不能使用this和super(没有父方法)
程序结束时静态域中的方法和属性才会被回收(不会消失)
关键字final
final修饰的类不能被继承(不能有子类)
比如Integer和String都是final修饰的
final修饰的属性为常量,必须初始化(可以直接初始化,也可以通过构造器初始化),并且要大写
static和final一起修饰的静态常量必须直接进行初始化
final修饰的方法不能被重写
抽象类(Abstract)
有抽象方法的类必须是抽象类,抽象类可以没有抽象方法(不一定全是抽象方法,也可以使用普通方法,普通方法可以不用重写)
抽象方法没有方法体,也就是没有大括号
抽象类:public abstract class A;
抽象方法:abstract void method();
有抽象类必然有子类
抽象方法(只有方法的定义,没有方法的实现)
子类必须重写父类中的抽象方法
抽象类不能实例化(创建对象)
抽象类一定是父类,并且一般大类别用抽象类
不能用abstract修饰属性、私有方法、构造器、静态方法、final的方法
举例:
abstract class A{
abstract void m1( );
public void m2( ){
System.out.println("A类中定义的m2方法");
}
}
class B extends A{
void m1( ){
System.out.println("B类中定义的m1方法");
}
}
public class Test{
public static void main( String args[ ] ){
A a = new B( );
a.m1( );
a.m2( );
}
}
输出:A类中定义的m2方法 B类中定义的m1方法
接口(interface)
接口其实就是相当于一个USB的插件,把它插到电脑上就可以用了
public interface 接口名{...}
接口中只有静态常量、抽象方法
接口没有构造器
定义接口的目的就是为了让实现类实现接口(面向接口编程)
声明接口 new 实现类
实现类必须重写接口中的所有方法,不想重写则可以是抽象类(加上abstract),如果实现类没有实现全部的抽象方法,那么实现类仍为抽象类,由子类继续来实现抽象方法
接口只能继承接口,并且是多继承
实现类可以先继承后实现
public class AImpl extends A implements B,C
interface Runner { public void run();}
interface Swimmer {public double swim();}
class Creator{public int eat(){…}}
class Man extends Creator implements Runner ,Swimmer{
public void run() {……}
public double swim() {……}
public int eat() {……}
}
接口与继承关系类似,接口与实现类之间也可以存在多态性
public class Test{
public static void main(String args[]){
Test t = new Test();
Man m = new Man();
t.m1(m);
t.m2(m);
t.m3(m);
}
public String m1(Runner f) { f.run(); }
public void m2(Swimmer s) {s.swim();}
public void m3(Creator a) {a.eat();}
}
匿名内部类
匿名内部类是没有名称的内部类。
在Java中调用某个方法时,如果该方法的参数是接口类型,除了可以传接口实现类外,还可以使用实现接口的匿名内部类作为参数,在匿名内部类中直接完成方法的实现。
public class TestDemo02 {
public static void main(String[] args) {
People people = new People();
people.method();
}
}
class People{
private String name;
public void method(){
//直接对这个接口中的方法进行重写并直接使用
class Student implements behavior{
@Override
public void eat() {
System.out.println("学生韩梅梅正在吃饭");
}
}
Student student = new Student();
student.eat();
class Teacher implements behavior{
@Override
public void eat() {
System.out.println("老师李华正在吃饭");
}
}
Teacher teacher = new Teacher();
teacher.eat();
}
}
interface behavior{
public void eat();
}