1、多态
多态简介
多态就是事物存在的多种形态,比如你在大街上看见一只藏獒,你可以说这只藏獒真凶猛,也可以说这只狗真凶猛,还可以说这个动物真凶猛,以上三种说法其实都是指的这只藏獒。
在Java里面,也是存在多态的,只要全部符合下面这三种情况,就是多态
- 有继承
- 有方法重写
- 有父类引用指向子类对象
例如下面代码就构成多态
class Animal{ public int num = 10; public void eat(){ System.out.println("动物再吃!"); } } class Cat extends Animal{ public int num = 20; public void eat(){ System.out.println("猫再吃!"); } } class Animal_Text{ public static void main(String[] args){ Animal m1 = new Cat();// 父亲引用指向子类对象 m1.eat(); // 猫在吃 动态绑定和静态绑定 System.out.println(m1.num); // 10 说明成员变量不存在 } }
静态绑定和动态绑定
上面代码中,a1是Animal类型的一个引用,指向的是其子类Cat的对象,这个就叫做父类引用指向子类对象。程序在编译的时候a1被看做Animal类型,所以a1.eat()绑定的是Animal类中的eat()方法,这叫做静态绑定,程序运行时,a1指向的是堆中的Cat对象,而在Cat中对eat()方法进行了重写,所以在运行阶段绑定的是Cat中的eat()方法,这叫做动态绑定。
强制类型转换
上面代码中子类向父类型进行转换,是自动类型转换,也叫做向上转型。还有一种情况是父类向子类型转换,是强制类型转换,也叫向下转型。下面的代码演示了强制类型转换
class Animal{ public int num = 10; public void eat(){ System.out.println("动物再吃!"); } } class Cat extends Animal{ public int num = 20; public void eat(){ System.out.println("猫再吃!"); } //Cat特有的方法. public void move(){ System.out.println("猫走路很轻盈!"); } } class Dog extends Animal{ //重写 public void eat(){ System.out.println("狗啃骨头!"); } } class Animal_Text{ public static void main(String[] args){ Animal a1 = new Cat();// 父亲引用指向子类对象 //如果要是想执行Cat里面的move方法该怎么办? //只能强制类型转换,需要加强制类型转换符 Cat c1 = (Cat)a1; c1.move(); Animal a2 = new Dog(); //向上转型. //强制类型转换 //Cat c2 = (Cat)a2; //会报错 java.lang.ClassCastException } }
instanceof关键字
上面的代码里面将一个指向Dog对象的Animal引用a2进行强制转换成Cat类型时报出了ClassCastException类转型错误,开发中要是想避免这种错误需要使用instanceof来判断一下。
class Animal{ public int num = 10; public void eat(){ System.out.println("动物再吃!"); } } class Cat extends Animal{ public int num = 20; public void eat(){ System.out.println("猫再吃!"); } //Cat特有的方法. public void move(){ System.out.println("猫走路很轻盈!"); } } class Dog extends Animal{ //重写 public void eat(){ System.out.println("狗啃骨头!"); } } class Animal_Text{ public static void main(String[] args){ Animal a1 = new Cat();// 父亲引用指向子类对象 //如果要是想执行Cat里面的move方法该怎么办? //只能强制类型转换,需要加强制类型转换符 Cat c1 = (Cat)a1; c1.move(); Animal a2 = new Dog(); //向上转型. //进行强制类型转换时,需要先使用instanceof进行判断,避免ClassCastException if (a2 instanceof Cat){ //强制类型转换 Cat c2 = (Cat)a2; }else{ System.out.println("无法进行强制类型转换"); } } }
多态的优点
- 提高了程序的扩展性
- 降低了代码之间的耦合
class Car{ public void run(){ System.out.println("汽车在行驶!"); } } class Benz extends Car{ public void run(){ System.out.println("奔驰汽车在跑"); } } class BWM extends Car{ public void run(){ System.out.println("宝马汽车再跑!"); } } class Person{ /* public void drive(Benz bc){ bc.run(); } 奔驰汽车坏了,再重新创建一个开宝马汽车的方法 public void drive(BMW bm){ bm.run(); } */ //上面代码扩展性太差,每新增加一种品牌的汽车就需要再写一个方法 //将参数修改为Car类型,这样不论增加什么样的品牌汽车,都可以调用这个方法 public void driver(Car c){ c.run(); } } public class Test{ public static void main(String[] args){ Person james = new Person(); //Benz bc = new Benz(); //james.drive(bc); BMW bm = new BMW(); james.drive(bm); } }
2、final关键字
final的特点
final的中文意思是最终,既然是最终就是已经结束了,无法再改变了。在Java里面final关键字同样也有着类似的功能。
- final修饰的类无法被继承。
- final修饰的方法无法被重写。
- final修饰的局部变量,一旦赋值,不可再改变。
- final修饰的成员变量必须初始化值。
public class Final{ public static void main(String[] name){ } } // final修饰的类无法被继承。 final class A1{ } class A2 extends A1{ } //final修饰的方法无法被重写。 class B1{ public final void method(){ } } class B2 extends B1{ public void method(){ } } //final修饰的局部变量,一旦赋值,不可再改变。 class C1{ public void C2(){ final int x = 1; x = 2; } } //final修饰的成员变量必须初始化值。 class D{ //final修饰的成员变量必须手动初始化. final int D1 = 100; //final修饰的成员变量一般和static联用。 ////java规范中要求所有的常量"大写" final static int D2 = 200; }
final修饰引用类型
final修饰的引用类型,该引用不可再重新指向其他的java对象。但是fianl修饰的引用,该引用指向的对象的属性值是可以修改的。
- 基本类型,是值不能被改变
- 引用类型,是地址值不能被改变,对象中的属性可以改变
public class FinalTest01{ public static void main(String[] args){ final Customer c = new Customer("张三",20); //c是final的,无法重新赋值。 //c = new Customer("李四",21);//Error c.name = "王五"; c.age = 25; System.out.println(c.name); System.out.println(c.age); } } class Customer{ String name; int age; Customer(String name,int age){ this.name = name; this.age = age; } }
3、抽象类
抽象的概念
抽象这个词说白了就是看不懂,毕加索的画一般都是被称为抽象的。在java里面可以使用关键字abstract修饰一个类,这样的类被称为抽象类,abstract修饰的方法叫做抽象方法。抽象类或抽象方法一般也是看不懂的,因为里面可能根本就没有代码。
抽象类的特点
- 抽象类无法被实例化,无法创建抽象类的对象。
- 虽然抽象类没有办法实例化,但是抽象类也有构造方法,该构造方法是给子类创建对象用的。这也算是多态的一种。
- 抽象类中不一定有抽象方法,但抽象方法必须出现在抽象类中。
- 抽象类中的子类可以是抽象类,如果不是抽象类的话必须对抽象类中的抽象方法进行重写。
- 抽象类和抽象方法不能被final修饰
public abstract class A{ //构造方法 A(){ System.out.println("A...."); } // 抽象方法 public abstract void m1(); public static void main(String[] args){ //抽象类无法创建对象 // A a = new A(); //多态 A a = new B(); } } class B extends A{ public void m1(){ } B(){ super(); //父类的构造方法虽然调用了,但是并没有创建父类对象。 System.out.println("B...."); } }
4、接口
接口的概述
电脑上面的主板有很多接口,比如内存条的接口,有了这个接口,可以插入多个内存条,主板和内存条可能不是同一家生产厂商,但是两种物体却能结合到一起,正是因为这个接口的存在。只要厂家遵循这个接口,主板和内存条就可以随意更换,提高了可插拔性,接口其实也是体现着一种规范。
在java语言里面使用interface
来声明一个接口,接口其实是一个特殊的抽象类,在接口里面的方法全部都是抽象的。
关于接口,有几个需要注意的地方:
- 接口中只能出现常量和抽象方法
- 接口里面没有构造方法,无法创建接口的对象
- 接口和接口之间支持多继承,即一个接口可以有多个父接口
- 一个类可以实现多个接口,即一个类可以有多个父接口
- 一个类如果实现了接口,那么这个类需要重写接口中所有的抽象方法(建议),如果不重写则这个类需要声明为抽象类(不建议)
public interface A{ //常量(必须用public static final修饰) public static final double PI = 3.14; //public static final是可以省略的. //double PI = 3.14; //抽象方法(接口中所有的抽象方法都是public abstract) public abstract void m1(); //public abstract是可以省略的. void m2(); } interface B{ void m1(); } interface C{ void m2(); } interface D{ void m3(); } interface E extends B,C,D{ void m4(); } //implements是实现的意思,是一个关键字. //implements和extends意义相同。 class MyClass implements B,C{ public void m1(){} public void m2(){} }
接口的作用
- 可以使项目分层,都面向接口开发,提高开发效率
- 降低了代码之间的耦合度,提高了代码的可插拔性
开发中尽量使用接口,少用抽象类,一个类可以实现多个接口,却只能继承一个父类
将之前的james开汽车的例子修改一下
将Car定义为接口
interface Car { public void run(); }
创建Benz和BMW类去实现这个接口
class Benz implements Car { public void run(){ System.out.println("奔驰汽车在跑"); } } class BMW implements Car { public void run(){ System.out.println("宝马汽车在跑"); } }
Person类不变
class Person { public void drive(Car c){ c.run(); } }
测试类不变
public class Test01 { public static void main(String[] args) { Person james = new Person(); Benz bc = new Benz(); //james.drive(bc); //BMW bm = new BMW(); james.drive(bc); } }
5、Object类之equals方法
equals方法
Java对象中的equals方法的设计目的:判断两个对象是否一样。
Java源码里面Object中的equals方法:
public boolean equals(Object obj) { return (this == obj); }
== 两边如果是引用类型,则比较内存地址,地址相同则是true,反之则false.
Object中的equals方法比较的是两个引用的内存地址。但是在现实的业务逻辑当中,不应该比较内存地址,应该比较地址里面的内容,所以需要对equals方法进行重写。
注意:在使用自己创建的类进行equals比较时,一定要先重写equals方法
class Car{ int id; String name; public Star(int id,String name){ this.id = id; this.name = name; } //根据需求规定重写equals方法 public boolean equals(Object obj){ if (this == obj){ return true; } if (obj instanceof Star){ Star s = (Star)obj; if(s.id == id && s.name.equals(name)){ return true; } } return false; } } public class Test01{ public static void main(String[] args){ Object o1 = new Object(); Object o2 = new Object(); boolean b1 = o1.equals(o2); System.out.println(b1); Star s1 = new Star(100,"成龙"); Star s2 = new Star(100,"成龙"); Star s3 = new Star(100,"李连杰"); System.out.println(s1.equals(s2)); System.out.println(s2.equals(s3)); } }
比较两个String类型时,不能使用==,要使用equals方法,String已经重写了Object中的equals方法,比较的是内容。
public class Test02{ public static void main(String[] args){ String s1 = new String("ABC"); String s2 = new String("ABC"); System.out.println(s1==s2); //false //String已经重写了Object中的equals方法,比较的是内容。 System.out.println(s1.equals(s2)); //true } }
6、finalize方法
finalize方法
finalize方法不需要程序员去调用,由系统自动调用。java对象如果没有更多的引用指向它,则该java对象成为垃圾数据,等待垃圾回收器的回收,垃圾回收器在回收这个java对象之前会自动调用该对象的finalize方法。finalize方法是该对象马上就要被回收了,例如:需要释放资源,则可以在该方法中释放。
public class Test01{ public static void main(String[] args){ Person p1 = new Person(); p1 = null; //没有引用再指向它.等待回收. //程序员只能“建议”垃圾回收器回收垃圾. System.gc(); } } class Person{ //重写Object中的finalize方法. public void finalize() throws Throwable { System.out.println(this + "马上就要被回收了!"); //让引用再次重新指向该对象,该对象不是垃圾数据,不会被垃圾回收器回收! //Person p = this; } }
7、package(包)和import
在日常生活中有很多同名的人,为了将这些同名的人进行区分,就出现了身份证,每个人的身份证号都是不一样的。在Java语言里面,开发者难免会编写出同名的类,为了区分出不通人开发出来的类,Java引入了包的概念。
使用package声明包名
在类名前面使用关键字package加入包名来避免命名冲突问题,因为域名是世界上唯一的,所以建议使用公司倒写的域名来命名包名,通常是小写的
例如:package com.monkey1024.score.system
上面包名的含义是monkey1024公司开发的score项目(学生成绩管理项目),system是score项目里面的一个模块。
假设这个score项目里面有学生模块、老师模块,可以这样进行命名:
学生模块:com.monkey1024.score.student
在学生模块的包里面,可以放置一些学生相关的类,比如AddStudent.class、DeleteSudent.class
老师模块:com.monkey1024.score.teacher
在老师模块的包里面,可以放置一些老师相关的类,比如AddTeacher.class、DeleteTeacher.class
其实这个包名就是文件夹的名称,如果按照上述命名,假设在我存放在电脑的f盘里面,F:commonkey1024scorestudentAddStudent.class
注意:
- package语句只能出现在.java源文件的第一行
- package语句在一个java文件中只能有一个
- 如果没有package,默认表示无包名
package com.monkey1024.oa.student; public class AddStudent{ public void add(){ System.out.println("添加学生"); } } package com.monkey1024.oa.student; public class Test01{ public static void main(String[] args){ AddStudent as = new AddStudent(); as.add(); System.out.println(as); } }
带包类的编译和运行
使用javac命令编译时,加上-d
例如:javac -d . HelloWorld.java
上面的.表示当前路径
运行时,使用java 包名.HelloWorld
需要加上包名
使用import关键字导入不同包下的类
将上面的Test01的包名修改一下
package com.monkey1024.oa.system; public class Test01{ public static void main(String[] args){ AddStudent as = new AddStudent();//报错找不到类 as.add() System.out.println(as); } }
上面代码将会报错,因为两个类在不同的包里面,在Test01这个包里面,系统找不到AddStudent类,所以前面需要加上包名:
com.monkey1024.oa.student.AddStudent as = new com.monkey1024.oa.student.AddStudent();
每次用到这个类时都需要写上包名,比较繁琐,我们可以使用import关键字将不同包下的类导入
package com.monkey1024.oa.system; import com.monkey1024.oa.student.*//导入这个包下的所有类 import com.monkey1024.oa.student.AddStudent//导入这个包下的AddStudent类,建议使用这种方式 public class Test01{ public static void main(String[] args){ AddStudent as = new AddStudent();//这样就没问题了 as.add() System.out.println(as); } }
注意:java.lang软件包下所有类不需要手动导入,系统自动导入,Object类,String类都在这个包里面
8、访问控制权限
4种访问控制权限
java访问级别修饰符主要包括:private 、protected、public和default(默认),可以限定其他类对该类、属性和方法的使用权限。
注意以上对类的修饰只有:public和default,内部类除外
priavte和public都比较好理解和记忆,这里就不演示了,主要演示一下不同包下的两个具有父子关系的类里面使用protected和default的区别。
创建一个Person类
package com.monkey1024.score.sys; public class Person{ String name; protected int age; void m1(){ System.out.println("m1"); } protected void m2(){ System.out.println("m2"); } }
创建一个User类,与Person类不在同一个包下
package com.monkey1024.score.buss; import com.monkey1024.score.sys.Person; public class User extends Person{ public void m3(){ m1();//无法访问,因为父类里面是default修饰的 m2(); System.out.println(age); System.out.println(name);//无法访问,因为父类里面是default修饰的 } }
9、使用Eclipse开发HelloWorld
详情请看:http://www.monkey1024.com/javase/358
10、eclipse的基本配置
详情请看:http://www.monkey1024.com/javase/379
11、eclipse常用快捷键
Elipse常用快捷键
- 新建 ctrl + n
- 格式化 ctrl+shift+f
代码格式错乱时,可以使用这个快捷键修复 - 导入包 ctrl+shift+o
- 注释 ctrl+/(添加和删除单行注释),ctrl+shift+/(添加多行注释),ctrl+shift+(删除多行注释)
- 代码上下移动 选中代码alt+上/下箭头
- 查看源码 选中类名(F3或者Ctrl+鼠标点击)
- 查找具体的类 ctrl + shift + t
在项目里面根据类名查找类 - 查找具体类的具体方法 ctrl + o
- 给建议 ctrl+1,根据右边生成左边的数据类型,生成方法
- 删除代码 ctrl + d
- 抽取方法alt + shift + m
将选中的代码抽取成一个方法 - 改名alt + shift + r
- 纵向选择alt + shift + a
- 查找文字ctrl + h
- 生成构造方法alt + shift + s 菜单显示之后再按c
- 生成get和set方法alt + shift + s 菜单显示之后再按r
- 关闭某个类ctrl + w
- 查看提示alt + /
12、eclipse导入导出项目
详情请看:http://www.monkey1024.com/javase/397
13、Eclipse中debug的使用