Java学习笔记(一)面向对象(OOP)
面向对象:
面向过程编程:一堆方法,调来调去
面向对象编程:以对象为核心,围绕着对象做操作
面向接口编程:面向对象的基础之上,抽接口
好的代码:复用性好、可维护性好、可扩展性好、移植性好......
面向过程:实在
缺陷一:缺乏对数据的封装
缺陷二:数据和方法分离状态
面向对象:抽象
1.现实世界是由很多对象组成的
2.现实世界是先有对象,再抽出类。代码中先创建类,再创建对象
3.一个类可以创建多个对象,同一个类的多个对象,结构相同,数据不同
4.类是一种数据类型 只能包含:
1)描述对象所共有的特征:------变量。属性-----静的
2)对象所共有的行为:----------方法。行为-----动的
5.new后,成员变量有默认值
6.创建对象语法:
类名 引用 = new 类名();
其中:new 类名()是在创建对象
因对象为数据,所有声明引用来指代数据
7.访问成员变量、访问方法 ----通过点来操作,语法:
引用.成员变量
引用.方法名();
8.基本类型之间画等号----再赋值------身份证复印件
引用类型之间画等号----指向同一个对象------房子钥匙
9.null:空,表示没有指向对象
若引用的值为null,则不能再进行点操作,
否则会出现NullPointerException异常
8种基本类型:可以直接赋值
除了8种之外:
类、接口、数组----引用类型 都是通过new出的
1.基本:
直接赋值
变量装的就是确切的值
画等号----赋值
int num = 5;
2.引用:
new
变量装的是地址
画等号----指向同一个对象
3.引用类型数组
int[] arr = new int[4]; arr指向 一个int[]类型的地址, 这个地址的大小是4个int类型的大小。
Cell[] cells = new Cell[4]; cells指向一个Cell[]类型的地址,这个地址的大小是4个Cell类型的大小
java建议:
1个文件只包含1个类
java规定:
java中一个文件可以包含多个类,
但是,public的类只能有1个,
并且,public的类必须与文件名相同
Java中的类
类可以看成是创建Java对象的模板。
public class Dog{ String breed; int age; String color; void barking(){ } void hungry(){ } void sleeping(){ } }
一个类可以包含以下类型变量:
- 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
- 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
- 类变量:类变量也声明在类中,方法体之外,但必须声明为static类型。
一个类可以拥有多个方法,在上面的例子中:barking()、hungry()和sleeping()都是Dog类的方法。
构造方法
1.构造方法常常用于给成员变量初始化
2.与类同名,没有返回值类型
3.构造方法是在创建对象时被自动调用
4.若自己不写构造方法,则编译器默认给一个无参构造,若自己写了,则不再默认提供无参构造
5.构造方法可以重载
public class Puppy{ public Puppy(){ } public Puppy(String name){ // 这个构造器仅有一个参数:name } }
创建对象
对象是根据类创建的。在Java中,使用关键字new来创建一个新的对象。创建对象需要以下三步:
- 声明:声明一个对象,包括对象名称和对象类型。
- 实例化:使用关键字new来创建一个对象。
- 初始化:使用new创建对象时,会调用构造方法初始化对象。
public class Puppy{ public Puppy(String name){ //这个构造器仅有一个参数:name System.out.println("小狗的名字是 : " + name ); } public static void main(String []args){ // 下面的语句将创建一个Puppy对象 Puppy myPuppy = new Puppy( "tommy" ); } }
/* 实例化对象 */ ObjectReference = new Constructor(); /* 访问类中的变量 */ ObjectReference.variableName; /* 访问类中的方法 */ ObjectReference.MethodName();
实例
下面的例子展示如何访问实例变量和调用成员方法:
public class Puppy{ int puppyAge; public Puppy(String name){ // 这个构造器仅有一个参数:name System.out.println("小狗的名字是 : " + name ); } public void setAge( int age ){ puppyAge = age; } public int getAge( ){ System.out.println("小狗的年龄为 : " + puppyAge ); return puppyAge; } public static void main(String []args){ /* 创建对象 */ Puppy myPuppy = new Puppy( "tommy" ); /* 通过方法来设定age */ myPuppy.setAge( 2 ); /* 调用另一个方法获取age */ myPuppy.getAge( ); /*你也可以像下面这样访问成员变量 */ System.out.println("变量值 : " + myPuppy.puppyAge ); } }
当在一个源文件中定义多个类,并且还有import语句和package语句时,要特别注意这些规则。
- 一个源文件中只能有一个public类
- 一个源文件可以有多个非public类
- 源文件的名称应该和public类的类名保持一致。例如:源文件中public类的类名是Employee,那么源文件应该命名为Employee.java。
- 如果一个类定义在某个包中,那么package语句应该在源文件的首行。
- 如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面。
- import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。
类有若干种访问级别,并且类也分不同的类型:抽象类和final类等。
this:
1.this指代当前对象,谁调指的就是谁
2.用法:
this.成员变量---访问成员变量
this.方法()-----访问方法
this()--------调构造方法
继承:
避免代码重复
父类中包含所有子类公有的数据
子类中包含子类所特有的数据
企鹅类:
public class Penguin { private String name; private int id; public Penguin(String myName, int myid) { name = myName; id = myid; } public void eat(){ System.out.println(name+"正在吃"); } public void sleep(){ System.out.println(name+"正在睡"); } public void introduction() { System.out.println("大家好!我是" + id + "号" + name + "."); } }
老鼠类:
public class Mouse { private String name; private int id; public Mouse(String myName, int myid) { name = myName; id = myid; } public void eat(){ System.out.println(name+"正在吃"); } public void sleep(){ System.out.println(name+"正在睡"); } public void introduction() { System.out.println("大家好!我是" + id + "号" + name + "."); } }
从这两段代码可以看出来,代码存在重复了,导致后果就是代码量大且臃肿,而且维护性不高(维护性主要是后期需要修改的时候,就需要修改很多的代码,容易出错),所以要从根本上解决这两段代码的问题,就需要继承,将两段代码中相同的部分提取出来组成 一个父类:
public class Animal { private String name; private int id; public Animal(String myName, int myid) { name = myName; id = myid; } public void eat(){ System.out.println(name+"正在吃"); } public void sleep(){ System.out.println(name+"正在睡"); } public void introduction() { System.out.println("大家好!我是" + id + "号" + name + "."); } }
企鹅类:
public class Penguin extends Animal { public Penguin(String myName, int myid) { super(myName, myid); } }
老鼠类:
public class Mouse extends Animal { public Mouse(String myName, int myid) { super(myName, myid); } }
继承的特性
-
子类拥有父类非private的属性,方法。
-
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
-
子类可以用自己的方式实现父类的方法。
-
Java的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如A类继承B类,B类继承C类,所以按照关系就是C类是B类的父类,B类是A类的父类,这是java继承区别于C++继承的一个特性。
-
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系)。
extends关键字
在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。
public class Animal { private String name; private int id; public Animal(String myName, String myid) { //初始化属性值 } public void eat() { //吃东西方法的具体实现 } public void sleep() { //睡觉方法的具体实现 } } public class Penguin extends Animal{ }
super 与 this 关键字
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
this关键字:指向自己的引用。
class Animal { void eat() { System.out.println("animal : eat"); } } class Dog extends Animal { void eat() { System.out.println("dog : eat"); } void eatTest() { this.eat(); // this 调用自己的方法 super.eat(); // super 调用父类方法 } } public class Test { public static void main(String[] args) { Animal a = new Animal(); a.eat(); Dog d = new Dog(); d.eatTest(); } }
输出结果为:
animal : eat
dog : eat
animal : eat
父类类型引用指向子类的对象---向上造型
动物是老鼠--------------语义不通
Mouse m = new Animal(); //错误
Person p = new Student(); //向上造型
Person p = new Teacher();
Person p = new Doctor();
public class Test { public static void main(String[] args) { Animal a = new Animal("动物"); Dog d = new Dog("小明"); sleep(a); sleep(d); } public static void sleep(Animal a){//可以接受任何Animal类型与Animal子类类型。 a.sleep(); //调用对应的方法 } } class Animal{ String name; public Animal(String name){ this.name = name; } public void sleep(){ System.out.println(name+"正在睡觉"); } } class Dog extends Animal{ public Dog(String name) { super(name); } public void sleep(){ System.out.println(name+"是条狗,"+"正在睡觉。"); } }
输出:
动物正在睡觉
小明是条狗,正在睡觉。
向上造型:父类引用指向子类对象
注意:能点出来什么,看类型
重写(override)和重载(overload)的区别------常见面试题
重载:
在一个类中,方法名相同,参数列表不同
重写:
在两个类中,并且是子类和父类的关系,签名相同
重载:编译时----.java到.class的过程
内存没东西---只看语法对不对
重写:运行时----jvm加载.class并运行.class的过程内存才有东西
堆、栈、方法区------jvm分配的内存
重载时调用看类型,重写时调用看对象
方法的重写:发生在两个类中,并且两个类是继承关系 。子类方法与父类方法的签名相同时 ---- 子类重写了父类的方法。
public class Test { public static void main(String[] args) { Coo c = new Coo(); Aoo a = new Boo(); c.print(a); } } class Aoo{ public void show(){ System.out.println("from Aoo"); } } class Boo extends Aoo{ public void show(){ System.out.println("from Boo"); } } class Coo{ public void print(Aoo a){ System.out.println("from Coo print(Aoo a)"); a.show(); } public void print(Boo b){ System.out.println("from Coo print(Boo b)"); b.show(); } }
输出:
from Coo print(Aoo a) from Boo
package 关键字
建议
域名反写 项目名称 模块名称
com.xxx.studentmanager.course.类名
package a.b.c.d.e; //声明包 public class Test{ } //a.Test----全局限定名 a.b.c.d.e.Test o = new a.b.c.d.e.Test(); import a.b.c.d.e.Test; //声明类、引入类 Test t = new Test(); //创建对象 import java.util.Arrays; import java.util.Random; import java.util.Scanner; import java.util.*; //---------不建议导入所有类
Scanner scan = new Scanner(System.in); java.util.Scanner scan = new java.util.Scanner(System.in);//也可以用全称。
同一个包中的类不需要import
访问修饰符:public,private,protected,默认
访问权限 类 包 子类 其他包
public ∨ ∨ ∨ ∨ (对任何人都是可用的)
protect ∨ ∨ ∨ × (继承的类可以访问以及和private一样的权限)
default ∨ ∨ × × (包访问权限,即在整个包内均可被访问)
private ∨ × × × (除类型创建者和类型的内部方法之外的任何人都不能访问的元素)
static:
成员变量:
1)实例变量-----不用static修饰的
2)静态变量-----static修饰的
何时用静态变量,何时用实例变量
class Customer{ //帐户类
String customerName; //帐户名称
String customerPwd; //帐户密码
static double poir; //利率
}
实例变量:属于对象,一个对象有一份
静态变量:属于类,所有对象公用这一份
class Aoo{ int a;-------------属于对象 static int b;------属于类 void show(){ b++; } }
类的方法中,常常需要对对象的实例变量操作
类的非静态方法,默认有个隐式的this
类的静态方法,没有隐式的this的
class Cell{ int row; //属于对象---实例变量 int col; //属于对象---实例变量 static int num; static void show(){ row++; //错误的 num++; //正确的 Cell c = new Cell(); c.row = 5; } void drop(){ this.row++; } void moveLeft(){ this.col--; } }
非静态方法:---有隐式this
可以直接访问静态变量和实例变量
需要访问实例变量时用
静态方法:-----没有隐式this
只能直接访问静态变量,不能直接访问实例变量
不需要访问实例变量时,只需对参数操作即可
何时用静态方法,何时用非静态方法:
静态方法,只与参数相关,与实例变量有关
Arrays.sort(arr);
Math.random();
Math.sqrt(25);
何时用静态代码块:
一般用于加载静态资源(图片、音频、视频)
static{ //静态代码块 }
final关键字
final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写:
-
声明类:
final class 类名 {//类体}
-
声明方法:
修饰符(public/private/default/protected) final 返回值类型 方法名(){//方法体}
-
Java中常量的设置:
public static final 数据类型 变量名;
注:实例变量也可以被定义为 final,被定义为 final 的变量不能被修改。被声明为 final 类的方法自动地声明为 final,但是实例变量并不是 final
abstract
1.抽象方法:由abstract修饰
只有方法的定义,没有方法体的
2.抽象类:由abstract修饰
可以包含抽象方法,也可以包含普通方法
3.包含抽象方法的类,必须是抽象类
类中没有抽象方法,也可以将类声明为抽象类
4.抽象类不能被实例化 Shape s = new Shape();//错误
5.抽象类一般需要被继承:
1)子类也声明为抽象类
2)子类重写抽象类中所有抽象方法---首选
6.抽象类的意义:
1)封装子类公用的成员 为子类提供一个公共的类型
2)定义抽象方法,由子类来做不同的实现,但入口(方法名)是一样的
abstract class Shape{ //抽象类(不完整) int c; //周长 abstract double area(); //抽象方法(不完整) } class Square extends Shape{ //方形类 Square(double c){ this.c = c; } double area(){ //重写 return 0.0625*c*c; } } class Circle extends Shape{ //圆形 Circle(double c){ this.c = c; } double area(){ //重写 return 0.0796*c*c; } }
interface:
1.接口就是一个标准、一个规范
2.接口中只能包含常量和抽象方法
3.接口不能被实例化
接口 引用 = new 实现类(); //向上造型
4.类实现接口,必须将所有抽象方法都实现
5.类可以实现多个接口,用逗号分隔
若类又继承父类又实现接口,需先继承后实现
6.接口与接口之间可以继承
接口与类相似点:
- 一个接口可以有多个方法。
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在 .class 结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
抽象类和接口的区别
- 1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
接口特性
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
interface 接口名称 [extends 其他的类名] { // 声明变量 // 抽象方法 }
接口有以下特性:
- 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
- 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
- 接口中的方法都是公有的。
- 接口中的方法默认带有修饰符 public abstract 常量默认带有修饰符 public static final
接口的实现
当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。
实现一个接口的语法,可以使用这个公式:
...implements 接口名称[, 其他接口名称, 其他接口名称..., ...] ...
interface Animal { public void eat(); public void travel(); }
public class MammalInt implements Animal{ public void eat(){ System.out.println("Mammal eats"); } public void travel(){ System.out.println("Mammal travels"); } public int noOfLegs(){ return 0; } public static void main(String args[]){ MammalInt m = new MammalInt(); m.eat(); m.travel(); } }
伪代码:
想开一个工商银行(对象) 工商银行类---------遵守标准(工行接口) 1.制定标准---接口 interface UnionPay{ //银联接口 void 存钱(); void 取钱(); void 改密码(); void 查余额(); } interface ICBC extends UnionPay{ //工行接口 void 在线支付(); } interface ABC extends UnionPay{ //农行接口 void 支付电话费(); } 2.遵守标准----类 class ICBCImpl implements ICBC{ //工行类 public void 存钱(){} public void 取钱(){} public void 改密码(){} public void 查余额(){} public void 在线支付(){} } class ABCImpl implements ABC{ //农行类 public void 存钱(){} public void 取钱(){} public void 改密码(){} public void 查余额(){} public void 支付电话费(){} } 3.main(){ ICBCImpl icbc1 = new ICBCImpl();//开了1个工行 icbc1.存钱()/取钱()/改密码()/查余额()/在线支付() ABCImpl abc1 = new ABCImpl(); ICBC i = new ICBCImpl(); //向上造型 }
重写接口中声明的方法时,需要注意以下规则:
- 类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。
- 类在重写方法时要保持一致的方法名,并且应该保持相同或者相兼容的返回值类型。
- 如果实现接口的类是抽象类,那么就没必要实现该接口的方法。
在实现接口的时候,也要注意一些规则:
- 一个类可以同时实现多个接口。
- 一个类只能继承一个类,但是能实现多个接口。
- 一个接口能继承另一个接口,这和类之间的继承比较相似。
接口的继承
一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。
// 文件名: Sports.java public interface Sports { public void setHomeTeam(String name); public void setVisitingTeam(String name); } // 文件名: Football.java public interface Football extends Sports { public void homeTeamScored(int points); public void visitingTeamScored(int points); public void endOfQuarter(int quarter); } // 文件名: Hockey.java public interface Hockey extends Sports { public void homeGoalScored(); public void visitingGoalScored(); public void endOfPeriod(int period); public void overtimePeriod(int ot); }
Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,这样,实现Hockey接口的类需要实现六个方法。
相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口。
接口的多继承
在Java中,类的多继承是不合法,但接口允许多继承。
在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口。 如下所示:
public interface Hockey extends Sports, Event
以上的程序片段是合法定义的子接口,与类不同的是,接口允许多继承,而 Sports及 Event 可能定义或是继承相同的方法
标记接口
最常用的继承接口是没有包含任何方法的接口。
标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。
标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。
例如:java.awt.event 包中的 MouseListener 接口继承的 java.util.EventListener 接口定义如下:
没有任何方法的接口被称为标记接口。标记接口主要用于以下两种目的:
- 建立一个公共的父接口:
正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。
- 向一个类添加数据类型:
这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。
什么时候使用抽象类和接口
- 如果你拥有一些方法并且想让它们中的一些有默认实现,那么使用抽象类吧。
- 如果你想实现多重继承,那么你必须使用接口。由于Java不支持多继承,子类不能够继承多个类,但可以实现多个接口。因此你就可以使用接口来解决它。
- 如果基本功能在不断改变,那么就需要使用抽象类。如果不断改变基本功能并且使用接口,那么就需要改变所有实现了该接口的类。
在 JDK1.8,允许我们给接口添加两种非抽象的方法实现:
1、默认方法,添加 default 修饰即可;
2、静态方法,使用 static 修饰;示例如下:
interface Test{ //这个是默认方法 default String get(String aa){ System.out.println("我是jdk1.8默认实现方法..."); return ""; } //这个是静态方法 static void staticmethod(){ System.out.println("我是静态方法"); } }