1、面向对象概述
面向过程注重过程,面向对象注重对象之间的交互,面向对象将复杂的事情简单化,我们从行动者变成了指挥者。面向对象开发就是去创建对象,指挥对象做事情。
面向对象的三大特征
- 封装()
- 继承
- 多态
2、类与对象
在Java语言里,最基本的单位是类(class),类是由成员变量和方法组成的。
类=成员变量+方法
成员变量:在类的内部,方法的外部定义的变量叫做成员变量。
局部变量:在方法的内部定义的变量叫作局部变量。
之前我们使用的都是局部变量。
我们可以将现实生活中的事物抽象成一个类,通过操作这个类来解决问题。
如何定义类:
类的修饰符 class 类名 extends 父对象名称 implements 接口名称{
类体,成员变量和方法组成
}
extends和implements还没有学过,目前我们定义类的格式是这样的:
类的修饰符 class 类名{
类体,成员变量和方法组成
}
这里根据上面分析来定义一个学生类:
public class Student
{
//成员变量可以不初始化
String name; //姓名
int age; //年龄
int id; //学号
boolean sex; //性别
//方法
public void study(){ //定义学习的方法
System.out.println("学生学习");
}
public void love(String name){ //定义谈恋爱的方法
System.out.println("我在跟"+name+"谈恋爱");
}
public void takeExcercise(String sport){
System.out.println("我在做"+sport+"运动");
}
}
学生类定义好了,那如何表示上面张三这个人呢?这时需要使用对象
类和对象
类是一组相关的属性和行为的集合,类一般是泛指某一种事物,对象就是该事物的具体体现。
比如:在这里插入代码片
类-学生
对象--张三
当然,对象可以是李四,王五,赵六。只要是学生就行。
再比如:
类--歌手
对象--周杰伦,刘德华
类--运动员
对象--姚明,刘翔
对象的创建与使用
(1) 创建对象的格式: 类名 对象名 = new 类名 ();
(2)对象名:是合法的标识符即可,建议使用驼峰命名法。
(3) 通过 对象名.变量名 的方式 使用成员变量。
(4) 通过 对象名.方法名()的方式 调用方法。
类定义完成后,对象的创建可通过new关键字创建,创建对象的过程通常被称为实例化。
new 类名();
eg: new Car(); //创建一个汽车对象。 = 你生产了一辆汽车
为了能够对实例化对象进行访问控制,需要用一个特殊的变量 —引用。
—引用类型变量可以存放该类对象的地址信息,通常称为 “指向该类的对象”。当一个引用类型变量指向该类的对象时,就可以通过这个变量对对象实施访问。
–除8种基本类型之外,用类、接口、数组、字符串等声明的变量都称为引用类型变量,简称“引用”。
Car car = new Car();
Car ---- 类
car ---- 引用,存放对象的地址。指向 new Car()对象
new Car() -----对象
通过引用就可以访问对象的成员变量和调用方法。
public class StudentTest {
public static void main(String[] args) {
//创建一个对象
Student s = new Student();
//通过对象名.变量名的方式使用成员变量
s.name = "战三";
s.age = 18;
s.sex = true;
s.id = 10001;
//通过 对象名.方法名(...)的方式 调用方法
s.study(); //学习的方法
s.love("咋啊");
s.takeExcercise("排球");
}
}
3、成员变量和局部变量
成员变量:写在类体的里面,方法体的外面,声明时可以不进行初始化值,可以被本类或其它类方法进行调用。
局部变量:写在方法体的里面,声明时必须进行初始化,只能在声明局部变量的方法内进行调用。
4、封装性和Private关键字
什么是封装?
封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
封装的优点:
隐藏代码的实现细节,提高安全性。
添加private修饰之后,外界无法直接访问成员变量,需要提高方法供外界访问即可。
class Student {
//使用private修饰符将age进行封装
//添加private修饰之后,外界无法直接访问age
//这时需要提供两个方法供外界访问即set和get 方法
private int age;
int id ;
String name;
//对外提供两个公开的方法
//set赋值
public void setAge(int _age){
age = _age;
}
//get读取
public int getAge(){
return age;
}
}
public class StudentTest2
{
public static void main(String[] args){
Student s1 = new Student();
s1.setAge(22);
System.out.println(s1.getAge());
}
}
private使用总结:
- 将成员变量用private修饰
- 提供对应的getXxx()和setXxx()方法
- private仅仅是封装的一种体现形式,封装不是私有
5、方法调用时参数传递问题
方法调用时,参数除了可以传递基本数据类型,还可以传递引用数据类型,这两种参数在传递的时候会有一些差异,在使用时需要注意。
基本数据类型参数传递:传递过去的是值(value).(不同的内存)
引用数据类型参数传递:传递过去的是引用(同一块内存)
6、构造方法
构造方法(constructor),有的地方叫作构造器或者构造函数。
构造方法的作用是给对象数据进行初始化。
构造方法的格式特点:
- 方法名与类名相同(注意大小写也要与类名一致)
- 没有返回值类型
- 没有void修饰
- 没有具体的返回值 return
- 如果一个类没有提供任何构造方法,系统默认提供无参数构造方法
- 如果一个类已经手动的提供了构造方法,那么系统不会在提供任何构造方法
6.1无参数构造方法的使用
class User {
private int age;
private String name;
public void setAge(int _age){
age = _age;
}
public void name(String _name){
name = _name;
}
public int getAge(){
return age;
}
public String getName(){
return name;
}
//构造函数(无参构造函数)
User(){
System.out.println("函数调用了无参的构造函数");
}
}
//创建对象
public class UserTest{
public static void main(String[] args){
User u1 = new User();
}
}
上面方法在创建User对象的时候,就打印出了构造方法里面的语句,说明在创建对象的时候会默认执行无参数构造方法。构造方法 不能 手动调用。
6.2有参数构造方法的使用
构造方法是可以重载的,可以写个有参数的构造方法为对象进行数据初始化。
class User {
private int age;
private String name;
public void setAge(int _age){
age = _age;
}
public void setName(String _name){
name = _name;
}
public int getAge(){
return age;
}
public String getName(){
return name;
}
//构造函数(无参构造函数)
User(){
System.out.println("函数调用了无参的构造函数");
}
//构造函数(含参数构造函数)
User(String _name,int _age){
name=_name;
age=_age;
}
}
//创建对象
public class UserTest{
public static void main(String[] args){
User u1 = new User(); //无参构造函数
User u2 = new User("周健",22); //含参构造函数
System.out.println(u2.getAge());
System.out.println(u2.getName());
}
}
7、this关键字
this 是java里面的一个关键字,是一种 引用类型,在堆(heap)中的每个java对象上都有一个this指向自己。this 代表着 当前对象的 引用。
this 可以作什么
7.1区分成员变量和局部变量
class Singer {
private String name;
private int id;
public String getName(){
return this.name;
}
//因为this代表当前对象的引用,所以可以使用 this.变量名的方式调用成员变量
public void setName(String name){
//this.name表示的是成员变量,name表示传入的参数
this.name = name;
}
//还可以使用this.方法名的方式调用当前对象的方法
//以下两种方式都可以调用到m2方法
public void m1(){
this.m2();
m2();
}
public void m2(){
System.out.println("Testing");
}
}
public class SingerTest{
public static void main(String[] args){
Singer s = new Singer();
s.setName("周健");
System.out.println(s.getName());
s.m1();//使用两种方式调用m2方法
}
}
7.2可以调用构造方法
语法:this(实参)
注意:如果要是使用this调用构造方法的话,那么this必须出现在构造方法的第一行。
class MyData {
private int year;
private int month;
private int day;
public int getYear(){
return year;
}
public int getMonth(){
return month;
}
public int getDay(){
return day;
}
public void setYear(int year){
this.year = year;
}
public void setMonth(int month){
this.month = month;
}
public void setDay(int day){
this.day = day;
}
MyData(){
//通过this调用有参的构造方法
this(1970,1,1);//必须出现在第一行,否则将编译报错
}
//构造方法
MyData(int year,int month,int day){
this.year = year;
this.month = month;
this.day = day;
}
}
public class myDataTest
{
public static void main(String[] args){
MyData md = new MyData();
System.out.println(md.getYear()+"年"+md.getMonth()+"月"+md.getDay()+"日");
}
}
在Java里面,局部变量和成员变量的标识符是可以相同的,如果有相同的标识符,则可以通过this调用成员变量。
8、static关键字
static的作用?
-
static可以修饰变量,被static修饰的变量叫作静态变量,程序运行时静态变量存放在方法区里面,因此,静态变量在类加载阶段赋值,并且只赋值一次。
-
static可以修饰方法,被static修饰的方法叫作静态方法,不用创建对象就能直接访问该方法,即使用类名.静态方法名的方式。静态方法不能访问非静态的数据,静态方法不能使用this.
-
static可以定义静态语句块,静态语句块在类加载阶段执行,并且只执行一次,并且是自上而下的顺序执行,在构造方法之前执行。
static修饰的变量、方法、代码块都是隶属于类(class)级别的,跟对象无关。某一类物体如果可以被多个其他物体所共享,那么可以将这类物体使用static修饰。
静态变量`
class Employee {
private String name;
static String company = "Alibaba";
public String getName(){
return name;
}
}
public class EmployeeTest
{
public static void main(String[] args){
Employee e1 = new Employee();
//可以通过对象名.静态方法名的方式访问,工作中不这样使用
System.out.println(e1.company);
//可以通过 类名.静态方法名的方式访问,工作中使用这种写法
System.out.println(Employee.company);
e1 = null;
System.out.println(e1.company); //不会报空指针,说明静态变量跟对象无关
}
}
静态方法
class StaticTest02 {
//成员变量
int i;
//成员方法
//成员方法必须使用 “引用.”格式
public void m1(){
System.out.println("m1方法");
}
//静态方法
//可以使用"类名."方式调用,也可以用"引用.",即使用的是"引用.",底层还是用"类名."
//静态方法中不能直接访问 非静态数据
//静态方法中不能使用this
public static void m2(){
//m1(); //错误,静态方法中不能访问非静态的方法
//System.out.println(i); //错误,静态方法中不能访问非静态的变量
System.out.println("m2方法");
}
}
public class Test
{
public static void main(String[]args){
StaticTest02 st = new StaticTest02();
st.m1();
st.m2(); //对象.方法名,不建议这样使用
StaticTest02.m2(); //类名.方法名,建议这样使用
st = null;
st.m2(); //不会报出空指针异常
}
}
静态语句块
public class StaticTest03{
static{
System.out.println("静态语句块1");
}
static{
System.out.println("静态语句块2");
}
static{
System.out.println("静态语句块3");
}
//构造方法
StaticTest03(){
System.out.println("构造方法");
}
public static void main(String[] args){
System.out.println("main main方法 1");
System.out.println("main main方法 2");
new StaticTest03();
new StaticTest03(); //静态语句块只执行一次
}
}
//---------- java ----------
静态语句块1
静态语句块2
静态语句块3
main main方法 1
main main方法 2
构造方法
构造方法
输出完成 (耗时 0 秒) - 正常终止
9、代码块的分类
使用{}括起来的代码被称为代码块,根据其位置和声明的不同 可以分为下面4种:
-
局部代码块,在方法中出现,限定变量生命周期,及早释放,提高内存利用率
-
构造代码块,在类中方法外出现;多个构造方法中相同的代码存放到一起,每次调用构造都执行,并且在构造方法前执行
-
静态代码块,在类中方法外出现,并加上static修饰;用于给类进行初始化,在加载的时候就执行,并且只执行一次。一般用于加载驱动
-
同步代码块(后面多线程部分会讲解)
方法执行顺序
1静态代码块,随着类加载而加载,且只执行一次
2构造代码块,每创建一个对象就会执行一次,优先于构造方法执行
3构造方法,每创建一个对象就会执行一次
class Student {
public Student(){
System.out.println("构造方法");
}
//构造代码块:每创建一次对象就会执行一次,优先于构造方法执行
{
System.out.println("构造代码块");
}
static{ //伴随着类加载而加载,且只执行一次
System.out.println("静态代码块");//作用:用来给类进行初始化,一般用来加载驱动
} //静态代码块是优先于主方法执行
}
public class BlockTest01
{
public static void main(String[] args){
//局部代码块
{
int x = 10;
System.out.println(x); //限定变量的生命周期
}
Student s1 = new Student();
System.out.println("------------------");
Student s2 = new Student();
}
- List item
static{
System.out.println("main方法类中的静态代码块");
}
}
10、继承extends
继承是面向对象三大特征之一。java中的继承描述的是两个类之间的关系,被继承的类称为父类,继承的类称为子类,使用extends关键字来表示。在java语言里面只支持单继承,即一个类只能有一个父类,子类可以继承父类中的非private修饰的成员方法和成员变量,构造方法不能被继承。
继承的优点:
-
提高了代码的复用性
-
提高了代码的维护性
-
让类与类之间产生了关系,是多态的前提
如何使用extends来实现继承关系?
10.1继承
[修饰符列表] class 子类名 extends 父类名{
类体;
}
首先,来自己定义一个父类SuperClass:
class Superclass {
public void m1(){
System.out.println("Superclass中的m1方法");
}
private void m2(){
System.out.println("Superclass中的m2方法");
}
}
然后定义一个子类SubClass来继承SuperClass
class Subclass extends Superclass
{
public void m3(){
System.out.println("Subclass中的m3方法");
}
}
写一个测试类来测试一下:
public class Test01
{
public static void main(String[] args){
Subclass s1 = new Subclass();
//因为Subclass继承了SuperClass,所以可以在子类里面调用父类的方法
s1.m1();
//s1.m2(); //子类不能访问父类中private修饰的方法
s1.m3();
}
}
//Java不支持多继承,但是支持多重继承,即 子类->父类->爷爷类-…->祖先类->Object类,子类可以访问其先辈里面的非private修饰的成员方法和成员变量。
10.2方法的重写(Override)
重写,也叫做覆盖,当父类中的方法无法满足子类需求时,子类可以将父类的方法重新编写来满足需求。比如孩子继承了父亲的房子,可以将房子重新装修。
方法重写的条件:
-
两个类必须是继承关系
-
必须具有相同的方法名,相同的返回值类型,相同的参数列表
-
重写的方法不能比被重写的方法拥有更低的访问权限、
-
重写的方法不能比被重写的方法抛出更宽泛的异常
-
私有的方法不能被重写
-
构造的方法不能被重写,因为构造方法无法被继承
-
静态方法不存在重写
-
重写指的是成员方法,和成员变量无关
先定义一个动物类:
class Animal {
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
定义一个Cat类继承Animal
class Cat extends Animal
{
public void eat(){
System.out.println("The cat is eating.....");
}
public void sleep(){
System.out.println("The cat is sleeping....");
}
}
定义一个Dog继承Animal
class Dog extends Animal{
public void eat(){
System.out.println("The dog is eating.....");
}
public void sleep(){
System.out.println("The dog is sleeping....");
}
}
Animal测试类
public class Test03
{
public static void main(String[] args){
Cat c = new Cat();
c.sleep();
c.eat();
Dog d = new Dog();
d.sleep();
d.eat();
}
}
上代码中的Cat类和Dog类在调用父类Animal中的eat方法时已经不能很好满足自身的需求了,所以都将eat类进行了重写。
静态的方法不存在重写
11Super关键字
super代表的是当前子类对象中的父类特征。
什么时候使用super?
-
子类和父类中都有某个数据,例如,子类和父类都有name这个属性。如果要在子类中访问父类中的name属性,需要使用super。
-
子类重写了父类的某个方法(假设这个方法名叫m1),如果子类中需要调用父类中的m1方法时,需要使用super
-
子类调用父类的构造方法时,需要使用super.
-
注意:super不能用在静态方法中
例1:首先定义一个Animal类
public class Animal
{
public String name = "动物";
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
定义一个Dog类继承Animal
class Dog extends Animal {
String name = "狗";
public void eat(){
System.out.println("吃狗粮");
}
public void m1(){
System.out.println(super.name); //调用父类的name变量
System.out.println(this.name); //可以不加this,系统默认调用子类自己的name
super.eat();//调用父类中的eat()方法
this.eat();
}
}
测试类:
public class AnimalTest01
{
public static void main(String[] args) {
Dog d = new Dog();
d.m1();
}
}
class Animal {
//颜色
String color;
//品种
String category;
public Animal(){ //无参构造方法
System.out.println("Animal 中的构造方法");
}
public Animal(String color,String category){ //含参构造方法
this.color = color;
this.category=category;
}
}
class Dog extends Animal
{
public Dog(){ //子类构造方法
//super("土豪金","藏獒"); //手动调用父类中的有参构造方法给成员变量进行赋值
//去掉父类含参构造方法会调用父类无参构造方法
System.out.println("Dog中的构造方法");
}
}
public class Test
{
public static void main(String[] args){
Dog d = new Dog();
System.out.println(d.color);
System.out.println(d.category);
}
}
注意:一个构造方法第一行如果没有this(…);也没有显示的去调用super(…);系统会默认调用super();如果已经有this了,那么久不会调用super了,super(…)的调用只能放在构造方法的第一行,只是调用了父类的构造方法,但是并不会创建父类的对象。
super与this的对比:
-
this和super分别代表什么
this:代表当前对象的引用
super:代表的是当前子类对象中的父类型特征 -
this和super的使用区别
调用成员变量 this.成员变量 :调用本类中的成员变量 super.成员变量 :调用父类的成员变量 调用构造方法: this(...):调用本类的构造方法 super(...):调用父类的构造方法 调用成员方法: this.成员方法 :调用本类的成员方法 super.成员方法:调用父类的成员方法
12、多态
12.1多态简介
多态就是事物存在的多种形态,比如你在大街上看见一只藏獒,你可以说这只藏獒真凶猛,也可说这只狗真凶猛,还可以说这个动物真凶猛,以上三种说法其实都是指的这只藏獒。
在Java里面,也是存在多态的,只要全部符合下面这三种情况,就是多态
-
有继承
-
有方法重写
-
有父类引用指向子类对象
class Animal{
int num =10;
public void eat(){
System.out.println("动物在吃!");
}
}
//定义一个Cat类继承Animal
class Cat extends Animal
{
int num = 20;
//重写
public void eat(){
System.out.println("猫吃猫粮");
}
//Cat特有的方法
public void move(){
System.out.println("猫走路很轻盈");
}
}
//Dog类继承Animal
class Dog extends Animal
{
//重写
public void eat(){
System.out.println("猫啃骨头");
}
}
//上面的三个类里面已经有继承和方法重写了,那么父类引用指向子类对象是什么?
//定义一个测试类
public class AnimalTest02
{
public static void main(String[] args){
Cat c1 = new Cat();
c1.eat();
Animal a1 = new Cat(); //父类引用指向子类对象
a1.eat(); //猫吃猫粮
System.out.println(a1.num); // 因为成员变量不存在重写,所以结果是10
}
}
动态绑定与静态绑定
上面代码中,a1是Animal类型的一个引用,指向的是其子类Cat的对象,这个就叫作父类引用指向子类对象。程序在编译的时候a1被看做Animal类型,所以a1.eat()绑定的是Animal类中的eat()方法,这叫作静态绑定,程序运行时,a1指向的是堆中的Cat对象,而在Cat中对eat()方法进行了重写,所以在运行阶段绑定的是Cat中的eat()方法,这叫做动态绑定。
强制类型转换
上面代码中子类向父类型进行转换,是自动类型转换,也叫做向上转型,还有一种情况是父类向子类型转换,是强制类型转换,也叫作向下转换。
public class AnimalTest02
{
public static void main(String[] args){
Animal a1 = new Cat(); //父类引用指向子类对象
a1.eat(); //猫吃猫粮
System.out.println(a1.num); // 因为成员变量不存在重写,所以结果是10
//如果要是想执行Cat里面的move方法该怎么办?
//只能强制类型转换,需要加强制类型转换符
Cat c1 = (Cat) a1;
c1.move();
Animal a2 = new Dog();//父类引用指向子类对象
//强制类型转换
//Cat c2 = (Cat)a2; //Exception in thread "main" java.lang.ClassCastException: Dog cannot be cast to Cat
}
}
instanceof关键字
上面的代码里面将一个指向Dog对象的Animal引用a2进行强制转换成Cat类型时爆出 .ClassCastException: 类型转型错误,开发中药是想避免这种错误需要使用instanceof来判断一下。
public class AnimalTest02
{
public static void main(String[] args){
Animal a1 = new Cat(); //父类引用指向子类对象
a1.eat(); //猫吃猫粮
System.out.println(a1.num); // 因为成员变量不存在重写,所以结果是10
//如果要是想执行Cat里面的move方法该怎么办?
//只能强制类型转换,需要加强制类型转换符
Cat c1 = (Cat) a1;
c1.move();
Animal a2 = new Dog();//父类引用指向子类对象,向上转型
//强制类型转换
if (a2 instanceof Cat)
{
//强制类型转换
Cat c2 = (Cat)a2;
}
//Cat c2 = (Cat)a2; //Exception in thread "main" java.lang.ClassCastException: Dog cannot be cast to Cat
}
}
多态的优点
- 提高了程序的扩展性
- 降低了代码之间的耦合
class Car
{
public void run(){
System.out.println("汽车在跑");
}
}
//创建一个Benz类继承Car
class Benz extends Car
{
public void run(){
System.out.println("奔驰汽车在跑");
}
}
//创建一个BMW类继承Car
class BMW extends Car
{
public void run(){
System.out.println("宝马汽车在跑");
}
}
//创建一个person类用来开汽车
class Person
{
/*
public void drive(Benz bc){
bc.run();
}*/
//奔驰汽车坏了,再重新创建一个开宝马汽车的方法
/*public void drive(BMW bm){
bm.run();
}*/
//上面代码扩展性太差,每新增加一种品牌的汽车就需要再写一个方法
//将参数修改为Car类型,这样不论增加什么样的品牌汽车,都可以调用这个方法
public void drive(Car c){
c.run();
}
}
//创建一个测试类
public class TestCar
{
public static void main(String[] args){
Person James = new Person();
Benz bc = new Benz();
James.drive(bc);
BMW bm = new BMW();
James.drive(bm);
}
}
在工作中尽量面向抽象编程,不要面向具体编程 。
13final关键字
final的中文意思是最终,既然是最终就是已经结束了,无法再改变了。在Java里面final关键字同样也有着类似的功能。
-
final修饰的类无法被继承
-
final修饰的方法无法被重写
-
final修饰的局部变量,一旦赋值,不可再改变
-
final修饰的成员变量必须初始化值
final修饰的类无法被继承
final class A{
}
class B extends A //FinalTest.java:4: 错误: 无法从最终A进行继承
{
}
final修饰的方法无法被重写
class A{
public final void m1(){}
}
class B extends A //FinalTest.java:4: 错误: 无法从最终A进行继承
{
public void m1(){} //B.java:7: 错误: B中的m1()无法覆盖A中的m1()
}
final修饰的局部变量,一旦被赋值,不可再改变
class A{
public void m1(){
//声明
final int i;
//第一次赋值
i = 100;
i =1200; //B.java:8: 错误: 可能已分配变量i
}
}
final修饰的成员变量必须初始化值
class A{
//final修饰的成员变量必须手动初始化
final int i = 100;
//error必须进行初始化
//final int k; //B.java:5: 错误: 变量 k 未在默认构造器中初始化
//final修饰的成员变量一般和static联用
//java规范中要求所有的常量"大写"
public static final double PI = 3.14;
}
final修饰引用类型
final修饰的引用类型,该引用不可再重新指向其他的java对象。但是final修饰的引用,该引用指向的对象的属性值是可以改变的。
- 基本类型,是值不能被改变
- 引用类型,是地址值不能被改变,对象中的属性可以改变
class Customer {
String name;
int age;
Customer(String name,int age){
this.name = name;
this.age = age;
}
}
public class CustomerTest
{
public static void main(String[] args){
final Customer c = new Customer("周健",12);
//c是final,无法重新赋值
//c = new Customer("张三",20); //CustomerTest.java:17: 错误: 无法为最终变量c分配值
//引用类型。是地址值不能被改变,对象中的属性可以改变
c.name = "王五";
c.age = 25;
System.out.println(c.name);
System.out.println(c.age);
}
}
14、抽象类
在java里面可以使用关键字abstract修饰一个类,这样的类被称为抽象类,abstract修饰的方法叫作抽象方法。抽象类或抽象方法一般也是看不懂的,因为里面可能根本就没有代码。
抽象类的特点
-
抽象类无法被实例化,无法创建抽象类的对象
-
虽然抽象类没有办法实例化。但是抽象类也有构造方法。该构造方法是给子类创建对象用的。这也算是多态的一种
-
抽象类中不一定有抽象方法,但抽象方法必须出现在抽象类中
-
抽象类中的子类可以是抽象类,如果不是抽象类的话必须对抽象类中的抽象方法进行重写
-
抽象类和抽象方法不能被fianl修饰
public abstract class A{
//构造方法
A(){
System.out.println("A......");
}
//抽象方法
public abstract void m1();
public static void main(String[] args) {
//A a = new A(); //A.java:13: 错误: A是抽象的; 无法实例化
//多态
A b = new B();
}
}
class B extends A
{
public void m1(){
}
B(){
super(); //父类的构造方法虽然调用了,但是并没有创建父类对象
System.out.println("B.........");
}
}
15、接口
在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();
}
//接口B
interface B
{
void m1();
}
//接口C
interface C
{
void m2();
}
//接口D
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();
}
//c创建Benz和BMW类去实现这个接口
class BMW implements Car
{
public void run(){
System.out.println("宝马汽车在跑");
}
}
//Benz接口
class Benz implements Car
{
public void run(){
System.out.println("奔驰汽车在跑");
}
}
//定义Person类
class person
{
public void drive(Car c){
c.run();
}
}
//定义测试类
public class interfaceTest{
public static void main(String[] args){
person James = new person();
Benz bc = new Benz();
James.drive(bc);
}
}
16、JAVA API
API是Application Programming Interface的缩写,中文意思是应用程序编程接口。sun公司提供了一些预先定义好的类和方法以供Java开发者使用,有了API,开发者可以在不查看源码的情况下了解某个类或者某个方法的使用方式。
通过查询Java API,我们可以学习很多东西,建议大家在开发中遇到Java提供的不认识类时候,多去查询一下API,利于各位技术的成长。
17、Object类
17.1、Object类之toString方法
Sun在Object类中设计toString方法的目的:返回java对象的字符串表示形式
在现实的开发过程中,Object中的toString方法就是要被重写的
public String toString()
class Person {
String name;
int age;
Person(String name, int age){
this.name = name;
this.age = age;
}
//重写toString方法
public String toString(){
return "Person[name="+name+",age="+age+"]";
}
}
public class PersonTest{
public static void main(String[] args){
Person p = new Person("周健",18);
System.out.println(p.toString()); //没重写前 Person@15db9742;重写后:Person[name=周健,age=18]
//创建一个Object类型对象
Object o1 = new Object();
System.out.println(o1.toString()); //java.lang.Object@15db9742
//print方法后面括号中如果是一个引用类型,会默认调用引用类型的toString方法
System.out.println(p); //Person[name=周健,age=18]
}
}
如果直接打印一个引用数据类型的对象,系统会默认调用其toString方法
17.2、Object类之equals方法
java对象中的equals方法的设计目的:判断两个对象是否一样
Java源码里面的Object中的equals方法:
public boolean equals(Object obj) {
return (this == obj);
}
==两边如果是引用类型,则比较内存,地址相同则是true,反之则是false.
Object中的equals方法比较的是两个引用的内存地址,但是在现实的业务逻辑当中,不应该比较内存地址,应该比较地址里面的内容,所以需要对equals方法进行重写。
注意:在使用自己创建的类进行equals比较时,一定要先重写equals方法
class star {
//身份证号
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 Test04
{
public static void main(String[] args){
Object o1 = new Object();
Object o2 = new Object();
boolean b1 = o1.equals(o2);
System.out.println(b1); //false
star s1 = new star(100,"成龙");
star s2 = new star (100,"成龙");
star s3 = new star (110,"李连杰");
System.out.println(s1.equals(s2)); //true
System.out.println(s2.equals(s3)); //false
}
}
比较两个String 类型时,不能使用==,要使用equals方法,String已经重写了Object中的equals方法,比较的是内容。
public class Test22{
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
}
}
17.3、Object类之finalize方法
finalize方法不需要程序员去调用,由系统自动调用。java对象如果没有更多的引用指向它,则该java对象成为垃圾数据,等待垃圾回收器的回收,垃圾回收器在回收这个java对象之前会自动调用该对象的finalize方法。finalize方法是该对象马上就要被回收了,例如:需要释放资源,则可以在该方法中释放。
18、Package(包)和import
包的概念:在Java语言里面,开发者难免会编写出同名的类,为了区分出不同人开发出来的类,Java引入了包的概念。
使用package声明包名
在类名前面使用关键字package加入包名来避免命名冲突,因为域名是世界上唯一的,所以建议使用公司倒写的域名来命名包名,通常是小写的。
例如: package com.monkey1024.score.system
上面包名的含义是monkey1024公司开发的score项目(学生成绩管理项目),system是score项目里面的一个模块。
假设这个score项目里面有学生模块、老师模块,可以这样进行命名:
学生模块: package com.monkey1024.score.student
在学生模块的包里面,可以放置一些学生相关的类,比如 AddStudent.class、DeleteStudent.class.
老师模块:package com.monkey1024.score.teacher
在老师模块的包里面,可以放置一些老师相关的类,比如AddTeacher.class、DeleteTeacher.class.
注意
- package语句只能出现在 .java源文件的第一行
- package语句在一个java文件中只能有一个
- 如果没有package,默认表示无包名
创建添加一个学生类:
package com.monkey1024.oa.student;
public class Student
{
public void add(){
System.out.println("添加学生");
}
}
创建一个测试类:
package com.monkey1024.oa.student;
public class test08{
public static void main(String[] args) {
Student as =new Student();
as.add();
System.out.println(as);
}
}
** 带包类的编译和运行**
使用javac命令编译时,加上 -d
例如: javac -d . HelloWorld.java
上面 的 . 表示当前路径
运行时,使用 java 包名.Helloworld
需要加上包名
使用import 关键字导入不同包下的类
将上面的Test08的包名修改一下
package com.monkey1024.oa.system;
public class test08{
public static void main(String[] args) {
Student as =new Student();
as.add();
System.out.println(as);
}
}
上面的代码将会报错,因为两个类在不同的包里面,在Test08这个包里面,系统找不到Student类,所以前面需要加上包名:
com.monkey1024.oa.student.Student as = new com.monkey1024.oa.student.Student();
每次用到这个类时都需要加上包名,比较繁琐,我们可以使用import关键字将不同包下的类导入:
import com.monkey1024.oa.student.* //导入这个包下所有的类,这种方式对内存要求较高
import com.monkey1024.oa.student.Addstudent //导入这个包下的Addstudent类,建议使用这种方式
19、访问控制权限
java 访问级别修饰符 主要 包括:private、protected、public和default(默认),可以限定其它类对该类、属性和方法的使用权限。
对类的修饰只有:public和default.
private和public都比较好理解和记忆。
创建一个Person类
package com.monkey1024.score.sys;
public class Person
{
String name; //默认访问权限 default
protected int age; //访问权限protected
void m1(){ //默认访问权限 default
System.out.println("m1");
}
protected void m2(){ //访问权限protected
System.out.println("m2");
}
}
创建一个User类,与Person类不在一个包下
package com.monkey1024.score.buss;
import com.monkey1024.score.sys.Person; //访问不同包下的类要导入import
public class User extends Person {
public void m3(){
m1(); //无法访问,因为父类里面是default修饰
m2();
System.out.println(age);
System.out.println(name); //无法访问,因为父类里面是default修饰的
}
}