文章目录
一、面向对象编程
面向对象(OOP)与面向过程(POP)
- 二者都是一种思想,面向对象是相对于面向过程而言的。
- 面向过程,强调的是功能行为。
- 面向对象,是将功能封装进对象,强调具备了功能的对象
- 面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法和原则,如抽象,分类,继承,聚合,多态等。
面向对象的三大特征
- 封装(Encapsulation)
- 继承(Inheritance)
- 多态(Polymorphism)
java类及类的成员
- 现实世界万事万物是由分子、原子构成的。同理,Java代码世界是由诸多个不同功能的类构成的。
- 现实世界中的分子、原子又是由什么构成的呢?原子核、电子!那么,Java中用类class来描述事物也是如此
属性:对应类中的成员变量
行为:对应类中的成员方法
面向对象的思想概述
- 可以理解为:类=汽车设计图;对象=实实在在的汽车
- 面向对象程序设计的重点是类的设计
- 定义类其实是定义类中的成员(成员变量和成员方法)
- 我要开车去丽江,这句话包含的类和方法有什么?
类:我,车
方法:我{开车(丽江){}}
车{启动(){} 停止(){} 前进(){} 后退(){} } - 体会以下几个经典案例涉及到的类和方法
列车司机紧急刹车
你把门关上了
二、类的写法
1、类的语法格式
修饰符 class 类名{
属性声明;
方法声明;
;
说明:修饰符 public:类可以被任意访问
类的正文要用{ }括起来
举例:
public class Person{
//属性成员变量,类的成员变量可以先声明,不用初始化,类成员变量有默认值
String name;//姓名,String的默认值是null
int age;//年龄,int的默认值是0
//行为,方法,也叫函数
public void showName(){
//方法的名称如果是多个单词,首个单词的首字母小写,其他的单词首字母大写,就像一个驼峰一样,所以叫驼峰命名法
System.out.println("姓名:" + name);
}
}
三、创建Java自定义类
步骤:
- 定义类(考虑修饰符、类名)
- 编写类的属性(考虑修饰符、属性类型、属性名、初始化值)
- 编写类的方法(考虑修饰符、返回值类型、方法名、形参等)
举例:
/**
*动物的类
*/
pubilc class Animal{
String name;//动物的名称
int eye;//眼睛都个数
int legs;//腿的个数
/**
*输出动物吃的食物
*/
public void eat(String food){
System.out.println("此类动物的食物是:" + food)
}
/**
*动物的移动方式
*/
public void move(String moveType){
Systme.out.println("此种动物的移动方式:" + moveType);
}
}
四、类的属性
1、 语法格式
修饰符 类型 属性名=初值
说明:
- 修饰符private:该属性只能由类的方法访问
- 修饰符public:该属性可以被该类以外的方法访问
- 类型:任何基本类型,如int、boolean或任何类型
举例:
public class Person{
pruvate int age; //声明private变量age
public String name ="Lila"; //声明public变量name
}
补:
变量的分类:成员变量与局部变量
- 在方法体外,类体内声明的变量称为成员变量
- 在方法体内部声明的变量称为局部变量
public class Person{
public String name = "zhangsan";
//public共有的,这样的变量可以在类的外部使用
private int age;
//private私有的,可以在本类的方法使用,不能做类的外部使用
//修饰符可以用来修饰变量,可以指明变量是一个共有还是私有,私有的类变量不能通过对象、属性的方式调用
//实例变量就是说在类实例化成对象之后才能使用的
public static String sex = "男";
//static,意思是静态的,这样的变量不需要类实例化成对象就可以使用
//直接就可以通过类名、属性这样的方式直接调用,这样的变量叫类变量
public void showwAge(){
System.out.println(age);
}
}
2、成员变量和(属性)和局部变量的区别
- 成员变量:
- 成员变量定义在类中,在整个类中都可以被访问
- 成员变量分为类成员变量和实例成员变量,实例变量存在于对象所在的堆内存中
- 成员变量有默认初始化值
- 成员变量的权限修饰符可以根据需要,任意选择一个
- 局部变量:
- 局部变量值定义在局部范围内,如:方法内,代码块内等
- 局部变量存在于栈内存中
- 作用的范围结束,变量空间会自动释放
- 局部变量没有默认初始化值,每次必须显示初始化
- 局部变量声明时不指定权限修饰符
3、成员变量格式
[修饰符] 类型 成员变量名 [= 初始值];
- 其中,修饰符中的访问权限可以为public、protected、private。
- 其他修饰符可以为final、static。
- 类型可以为任意的基本类型或引用类型
- 成员变量名同样为合法的标识符,一般采用小驼峰命名法来表示。
- 成员变量名通常为名词(如身高、体重)。
- 初始值可以省略,如不显式指定初始值则为该类型的默认值(数值型为0,布尔型为False,引用类型为null)。
五、类的方法
在 Java 中,方法必须定义在类中,不能单独存在。定义方法的统一格式如下:
[修饰符] 返回值类型 方法名( [形参列表] ) {
// ...
// 如果声明了返回值类型,必须有 return 语句
}
- 其中,修饰符(可省略)中的访问权限可以为public、protected、private。
- 其他修饰符可以为final|abstract、static。
- 返回值类型可以为任意的基本类型或引用类型,也可以为void(无返回值)。
- 方法名为合法的标识符,一般采用小驼峰命名法来表示。方法名通常为动词(如走、跑)。
六、关于对象
1、对象的产生
当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。除了基本数据类型之外的变量类型都是应用类型,如上面的Person及前面讲过的数组。
成员类型变量 | 初始值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0F |
double | 0.0D |
char | \‘\u0000\’(表示为空) |
boolean | flase |
应用类型 | null |
2、匿名对象
- 我们也可以不定义对象的的句柄,而直接定义这个对象的方法。这样的对象叫做匿名对象。如:new Person().shout():
- 使用情况:
如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
我们经常将匿名对象作为实参传递给一个方法调用。
提示
类的访问机制:
- 在一个类中访问机制:类中的方法可以直接访问类中的成员变量。
- 在不同类中的访问机制:先创建要访问类的对象,再使用对象访问类中定义的成员。
七、方法的重载
1 重载的概念
在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型不同即可。
2、重载的特点
与返回值类型无关,只看参数列表,且参数列表必须不同(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。
八、方法
1、方法的可变个数的参数
采用数组形参来定义方法:public static void test(int a,String[] books);
以可变个数形参来定义方法:public static void test(int a,String…books);
2、方法的参数传递
- 方法,必须有其所在类或对象调用才有意义。若方法含有参数:
形参:方法声明时的参数
实参:方法调用时实际传给寻更惨的参数值 - Java的实参值如何传入方法呢?
Java里方法的参数传递方式只有一种:传递值。即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
九、包package和引用import
1、软件包
- 包帮助管理大型软件系统:将语义近似的类组织到包中:解决类明明冲突问题
- 包可以包含类和子包
- 例:某航运软件系统包括:一组域对象、GUI和reports子系统
2、关键字——package
- package语句作为JAva源文件的第一条语句,指明该文件中定义的类所在的包。
- 包对应于文件系统的目录,package语句中,用“.”来指明包(目录)的层次
- 包通常用小写单词,类名首字母通常大写
3、关键字——import
未使用定义在不同包中的Java类,需要import语句来引入指定包层次下所需要的类或全部类。import语句告诉编译器到哪里去寻找类。
注意:
- 若引入的包为:java.lang,测编译器默认可获取·此包下的类,不需要再显式声明。
- import语句出现在package语句之后、类定义之前
- 一个源文件中可包含多个import语句
- 可以使用import lee.*;语句,表明导入lee包下的所有类。而lee包下sub字包内的类则不会被导入。
- import语句不是必须的,可坚持在类里使用其他类的全名
- JDK 1.5加入import static语句
十、构造方法
1、类的构造方法
- 构造器的特征:
它具有与类相同的名称
它不生命返回值类型
不能被static、final、synchronized、abstract、native修饰,不能有return语句返回值 - 构造器
隐式无参构造器
显式定义一个或者多个构造器 - 构造器的作用:
创建对象
给对象进行初始化
2、构造方法重载
- 构造器一般用来去创建对象的同时初始化对象。
- 构造器重载使得对象的创建更加灵活,方便创建各种不同的对象。
- 构造器重载,参数列表必须不同
十一、this关键字
- 在Java中,this关键字比较难理解,它的作用和其词义很接近。
- this表示当前对象,可以调用类的属性、方法和构造器
- 当在方法内需要用到该方法的对象时,就用this。
十二、高级类特性
1、单继承和多层继承
- Java只支持单继承,不允许多继承
一个子类只能有一个父类
一个父类能派生出多个子类 - 如果要直接继承类,子类不可直接多继承,但可通过多层继承实现多继承。但多层继承一般建议不超过三次,且代码较冗余。
2、方法的重写
在重写方法时,需要遵循下面的规则:
- 参数列表必须完全与被重写的方法参数列表相同。
- 返回的类型必须与被重写的方法的返回类型相同(Java1.5 版本之前返回值类型必须一样,之后的 Java 版本放宽了限制,返回值类型必须小于或者等于父类方法的返回值类型)。
- 访问权限不能比父类中被重写方法的访问权限更低。
- 重写方法一定不能抛出新的检査异常或者比被重写方法声明更加宽泛的检査型异常。
另外还要注意以下几条:
- 重写的方法可以使用 @Override 注解来标识。
- 父类的成员方法只能被它的子类重写。
- 声明为 final 的方法不能被重写。
- 声明为 static 的方法不能被重写,但是能够再次声明。
- 构造方法不能被重写。
- 子类和父类在同一个包中时,子类可以重写父类的所有方法,除了声明为 private 和 final 的方法。
- 子类和父类不在同一个包中时,子类只能重写父类的声明为 public 和 protected 的非 final 方法。
- 如果不能继承一个方法,则不能重写这个方法。
3、关键字super
super 关键字可以在子类的构造方法中显式地调用父类的构造方法,基本格式如下:
super(parameter-list);
其中,parameter-list 指定了父类构造方法中的所有参数。
在Java类中使用super来调用父类中的操做:
- super可用于访问父类中定义的属性
- super可用于调用父类中定义的成员方法
- super可用于在子类构造方法中调用父类的构造器
4、多态性
- 多态性是面向对象编程的又一个重要特征,它是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,这使得同一个属性或方法在父类及其各个子类中具有不同的含义。
- 对面向对象来说,多态分为编译时多态和运行时多态。其中编译时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的方法。通过编译之后会变成两个不同的方法,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是大家通常所说的多态性。
- Java 实现多态有 3 个必要条件:继承、重写和向上转型。只有满足这 3 个条件,开发人员才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而执行不同的行为。
- 继承:在多态中必须存在有继承关系的子类和父类。
- 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
- 向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才既能可以调用父类的方法,又能调用子类的方法。
5、instanceof操作符
instanceof
instanceof是Java的一个保留关键字,左边是对象,右边是类,返回类型是Boolean类型。它的具体作用是测试左边的对象是否是右边类或者该类的子类创建的实例对象,是,则返回true,否则返回false。
instanceof使用注意事项:
- 先有继承关系,再有instanceof的使用。
- 当该测试对象创建时右边的声明类型和左边的类其中的任意一个跟测试类必须得是继承树的同一分支或存在继承关系,否则编译器会报错。
6、Object类
Object 类的常用方法
方法 | 说明 |
---|---|
Object clone() | 创建与该对象的类相同的新对象 |
boolean equals(Object) | 比较两对象是否相等 |
void finalize() | 当垃圾回收器确定不存在对该对象的更多引用时,对象垃圾回收器调用该方法 |
Class getClass() | 返回一个对象运行时的实例类 |
int hashCode() | 返回该对象的散列码值 |
void notify() | 激活等待在该对象的监视器上的一个线程 |
void notifyAll() | 激活等待在该对象的监视器上的全部线程 |
String toString() | 返回该对象的字符串表示 |
void wait() | 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待 |
其中,toString()、equals() 方法和 getClass() 方法在 Java 程序中比较常用。
7、对象的类型转换
将一个类型强制转换成另一个类型的过程被称为类型转换。本节所说的对象类型转换,是指存在继承关系的对象,不是任意类型的对象。当对不存在继承关系的对象进行强制类型转换时,会抛出 Java 强制类型转换(java.lang.ClassCastException)异常。
Java 语言允许某个类型的引用变量引用子类的实例,而且可以对这个引用变量进行类型转换。Java 中引用类型之间的类型转换(前提是两个类是父子关系)主要有两种,分别是向上转型(upcasting)和向下转型(downcasting)。
(1)、向上转型
父类引用指向子类对象为向上转型,语法格式如下:
fatherClass obj = new sonClass();
其中,fatherClass 是父类名称或接口名称,obj 是创建的对象,sonClass 是子类名称。
向上转型就是把子类对象直接赋给父类引用,不用强制转换。使用向上转型可以调用父类类型中的所有成员,不能调用子类类型中特有成员,最终运行效果看子类的具体实现。
(2)、向下转型
与向上转型相反,子类对象指向父类引用为向下转型,语法格式如下:
sonClass obj = (sonClass) fatherClass;
其中,fatherClass 是父类名称,obj 是创建的对象,sonClass 是子类名称。
向下转型可以调用子类类型中所有的成员,不过需要注意的是如果父类引用对象指向的是子类对象,那么在向下转型的过程中是安全的,也就是编译是不会出错误。但是如果父类引用对象是父类本身,那么在向下转型的过程中是不安全的,编译不会出错,但是运行时会出现我们开始提到的 Java 强制类型转换异常,一般使用 instanceof 运算符来避免出此类错误。
十三、高级类特性2
1、关键字static
关键字static的作用是什么?
在C语言中,关键字static有三个明显的作用:
- 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
- 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
- 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。 大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数 据和代码范围的好处和重要性。
-
修饰局部变量
static修饰局部变量时,使得被修饰的变量成为静态变量,存储在静态区。存储在静态区的数据生命周期与程序相同,在main函数之前初始化,在程序退出时销毁。(无论是局部静态还是全局静态) -
修饰全局变量
全局变量本来就存储在静态区,因此static并不能改变其存储位置。但是,static限制了其链接属性。被static修饰的全局变量只能被该包含该定义的文件访问(即改变了作用域)。 -
修饰函数
static修饰函数使得函数只能在包含该函数定义的文件中被调用。对于静态函数,声明和定义需要放在同一个文件夹中。 -
修饰成员变量
用static修饰类的数据成员使其成为类的全局变量,会被类的所有对象共享,包括派生类的对象,所有的对象都只维持同一个实例。 因此,static成员必须在类外进行初始化(初始化格式:int base::var=10;),而不能在构造函数内进行初始化,不过也可以用const修饰static数据成员在类内初始化。 -
修饰成员函数
用static修饰成员函数,使这个类只存在这一份函数,所有对象共享该函数,不含this指针,因而只能访问类的static成员变量。静态成员是可以独立访问的,也就是说,无须创建任何对象实例就可以访问。例如可以封装某些算法,比如数学函数,如ln,sin,tan等等,这些函数本就没必要属于任何一个对象,所以从类上调用感觉更好。
2、单例设计模式
(1)、饿汉式
public class EagerSingleton {
// 静态变量,类在创建之初就会执行实例化动作。
private static EagerSingleton instance = new EagerSingleton();
// 私有化构造函数,使外界无法创建实例
private EagerSingleton(){}
// 为外界提供获取实例接口
public static EagerSingleton getInstance(){
return instance;
}
}
上面是饿汉式单例模式的标准代码,所谓的“饿汉式”只是形象的比喻:EagerSingleton类的实例因为变量instance申明为static的关系,在类加载过程中便会执行。由此带来的好处是Java的类加载机制本身为我们保证了实例化过程的线程安全性,缺点是这种空间换时间的方式,即使类实例本身还未用到,实例也会被创建。
(2)、懒汉式
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton(){}
// 为外界提供获取实例接口
public static LazySingleton getInstance(){
if(instance == null){
instance = new LazySingleton(); // 懒加载
}
return instance;
}
}
饿汉式和懒汉式的区别在于,饿汉式在类加载时便被实例化,而懒汉式是在getInstance()函数调用时,相信你也能看出来,当instance == null 时,去实例化,否则直接返回实例。
(3)、双重检查锁
public class Singleton {
private volatile static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
//先检查实例是否存在,如果不存在才进入下面的同步块
if(instance == null){
//同步块,线程安全的创建实例
synchronized (Singleton.class) {
//再次检查实例是否存在,如果不存在才真正的创建实例
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
所谓的双重检查,是在同步前后的两次if(instance == null)判断,是否已经存在实例,锁自然指的就是synchronized关键字。
(4)、静态内部类
public class Singleton {
private Singleton(){}
// 只有当类被调用时,才会加载
private static class SingletonHolder{
// 静态初始化器,由JVM来保证线程安全
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
- 线程安全:由静态内部类中的静态成员初始化时创建实例,通过JVM类加载机制来保证线程的安全性。
- 懒加载:使用静态内部类的方式,让类SingletonHolder只有在使用的时候才会被加载,实例才会创建,借机实现了懒加载。
(5)、枚举
public enum Singleton {
uniqueInstance;
public void singletonOperation(){
// 单例类的其它操作
}
}
3、关键字final
final关键字的基本用法:
在java中,final关键字可以用来修饰类、方法、变量(包括成员变量和局部变量)。下面我们从这三个方面了解一下final的用法
修饰类
final修饰一个类时,表示该类不能继承。final类中的成员变量可以根据需要设为final,但是final类中的所有成员方法都会被隐式地指定为final方法。
注意:在使用final修饰类的时候,一定要谨慎选择,除非这个类以后不会用来继承或者出于安全考虑,尽量不要将类设计为final类。
修饰方法
- 把方法锁定,以防任何继承类修改它的含义
- 父类的final方法是不能被子类所覆盖的,也就是说子类是不能够存在和父类一模一样的方法的。
修饰变量
final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。本质上是一回事,因为引用的值是一个地址,final要求值,即地址的值不发生变化。
4、接口
-
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
-
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
-
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
-
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口与类相似点:
- 一个接口可以有多个方法。
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在 .class 结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
接口特性 ;
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
抽象类和接口的区别:
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
接口的实现:
- 当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
- 类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。
5、内部类
(1)、非静态内部类
- 非静态内部类是一个类中嵌套着另外一个类。 它有访问外部类成员的权限, 通常被称为内部类。
- 由于内部类嵌套在外部类中,因此必须首先实例化外部类,然后创建内部类的对象来实现。
(2)、私有的内部类
- 内部类可以使用 private 或 protected 来修饰,如果你不希望内部类被外部类访问可以使用 private
(3)、静态内部类
- 静态内部类可以使用 static 关键字定义,静态内部类我们不需要创建外部类来访问,可以直接访问它。
(4)、从内部类访问外部类成员
- 内部类一个高级的用法就是可以访问外部类的属性和方法