Java帮帮-IT资源分享网
四、黑马程序员—面向对象(2)
四、黑马程序员—面向对象(2)
第四篇:
1、基本数据类型的包装类
引言:Java 提倡的万物皆对象,但是数据类型的划分出现了基本数据类型和引用数据类型,
那么我们怎么能把基本数据类型称为对象呢?
![图片](https://i-blog.csdnimg.cn/blog_migrate/be7e86e3d740f11d7413111518cd259d.png)
除了 Integer 和 Character 定义的名称和对应的基本类型差异大,其他六种都是将首字母大写
就可以了。
Integer,Byte,Float,Double,Short,Long 都是 Number 类的子类。(Number 类后面讲);
Character 和 Boolean 都是 Object 直接子类;
8 个类都是 final 修饰的(不可被继承)。
2、基本数据类型和包装类相互转换
把基本数据类型 → 包装类:
通过对应包装类的构造方法实现
除了 Character 外,其他包装类都可以传入一个字符串参数构建包装类对象。
包装类 → 基本数据类型
包装类的实例方法 xxxValue(); // xxx 表示包装类对应的基本数据类型
Eg:
boolean bool = false;
Boolean b2 = new Boolean(bool);
Integer i = new Integer(3);
int i2 = i.intValue();
Boolean b1 = new Boolean("TRue");//true
boolean b2 = b1.booleanValue();
Float f = new Float("3.14");//3.14
Integer i2 = new Integer("123s");//NumberFormatException
备注:
自动装箱&自动拆箱
jdk1.5 开始出现的特性:
自动装箱:可把一个基本类型变量直接赋给对应的包装类对象或则 Object 对象
自动拆箱:允许把 包装类对象直接赋给对应的基本数据类型
Eg:
Integer i = 3;//装箱
int i2 = i;//拆箱
Object flag = new Boolean(false);
if(flag instanceof Boolean){
Boolean b = (Boolean)flag;
boolean b2 = b;
}
我的总结:对于基本数据类型和包装类之间的装换,我们可以直接的用,相互转换,因为
java5 之后的自动拆箱、装箱功能!即便不知道这个,其实使用中也不影响!
3、基本类型和 String 之间的转换
String → 基本类型,除了 Character 外所有的包装类提供 parseXxx(String s)静态方法,用于
把一个特定的字符串转换成基本类型变量;
基本类型 → String,String 类有静态方法 valueOf(),用于将基本类型的变量转换成 String
类型。
String str = "17";
int i = Integer.parseInt(str);//String --> 基本类型
String s1 = String.valueOf(i);//基本类型 --> String
我的总结:这个从后续的学习来看,用处不大,记住有这样的方法就行,查 api!
4、Object 类
所有类的公共父类,一旦一个类没有显示地继承一个类则其直接父类一定是 Object。
一切数据类型都可用 Object 接收
class OOXX extends Object{}等价于 class ooXX {}
常见方法
public boolean equals(Object obj):对象比较
public int hashCode():取得该对象的 Hash 码
public String toString():对象描述
Object 类的 toString()方法:“对象的描述”
建议所有类都覆写此方法
直接打印输出对象时,会调用该对象的 toString()方法。//可以不写出来
打印对象的时候,实际调用的对象实际指向的类的自我描述;
全限定类名+@+十六进制的 hashCode 值,等价于
全限定类名+@+IntegertoHexString(该对象.hashCode)
equals 也是判断是否指向同一个对象
没有实际意义,有必要可以重写
public boolean equals(Object obj) {}
String 覆写了 Object 的 equals 方法:只比较字符的序列是否相同
==用于判断两个变量是否相等
基本类型:
引用类型:必须指向同一个对象,才 true
只能比较有父子或平级关系的两个对象
new String("1") == new String("1"); ?
5、代码块
代码块指的是使用"{}"括起来的一段代码,根据代码块存在的位置可以分为 4 种:
普通代码块;
构造代码块;
静态代码块;
同步代码块(线程同步的时候讲解)。
代码块里变量的作用域:
只在自己所在区域(前后的{})内有效;
普通代码块:
普通代码块就是直接定义在方法或语句中定义的代码块:
public void show(){
普通代码块
}
构造代码块:
直接写在类中的代码块:
优先于构造方法执行,每次实例化对象之前都会执行构造代码块。
Eg:
public class Demo {
{
System.out.println("我是构造代码块");
}
public Demo(){
System.out.println("我是构造方法");
}
public static void main(String[] args) {
Demo d1 = new Demo();
Demo d2 = new Demo();
}
}
静态代码块
使用 static 修饰的构造代码块:
优先于主方法执行,优先于构造代码块执行,不管有创建多少对象,静态代码块只执
行一次,可用于给静态变量赋值;
Eg:
package reviewDemo;
/**
* 测试各代码块的优先级
* 优先级顺序:静态代码块 > 构造代码块 > 普通代码块
* 备注:无论创建几个对象,静态代码块只执行一次!
*/
public class Demo13 {
Demo13(){
System.out.println("我是构造方法!");
}
{
System.out.println("我是构造代码块!");//实例化对象的时候才会去调
用!
}
static{
System.out.println("我是静态代码块!");
}
public static void main(String[] args) {
new Demo13();
new Demo13();//再次创建对象,证明无论创建几次对象,静态代码块都只执行
一次
System.out.println("我是普通代码块!");
}
}
输出:
我是静态代码块!
我是构造代码块!
我是构造方法!
我是构造代码块!
我是构造方法!
我是普通代码块!
我的总结:这个例子非常好!
6、构造方法的私有化
有的时候我们为了避免外界创建某类的实例,就将某类的构造方法私有化,即将它的构造方
法用 private 修饰:
外界如何用到?
提供 get 方法!不提供的话外界就没法创建对象!(对反射无效)
Eg:package reviewDemo;
class Stu{
//将构造方法私有化
private Stu(){
}
}
public class Demo15 {
public static void main(String[] args) {
Stu s = new Stu();
}
}
7、Singleton 模式(单例模式) 饿汉式和懒汉式
目的:整个应用中有且只有一个实例,所有指向该类型实例的引用都指向这个实例。
好比一个国家就只有一个皇帝(XXX),此时每个人叫的“皇帝”都是指叫的 XXX 本人;
常见单例模式类型:
饿汉式单例:直接将对象定义出来
懒汉式单例:只给出变量,并不将其初始化;
我的总结:
饿汉式,static 修饰,随着类的加载而加载,会损耗性能,但是方法相对简单
懒汉式 第一次用的时候相对较慢,因为需要加载!线程,不安全!
package reviewDemo;
//单例模式
//饿汉式,直接把对象构造出来
class SingleDemo{
private static SingleDemo s1 = new SingleDemo();
private SingleDemo(){
//提供私有化的构造方法,那么外界就不能构造对象了!
}
public static SingleDemo getS1() {
return s1;
}
}
//懒汉式,先定义,但是不创建对象
class SingleDemo2{
private static SingleDemo2 s3 ;
private SingleDemo2(){
//提供私有化的构造方法,那么外界就不能构造对象了!
}
public static SingleDemo2 getS3() {//这是一个方法,返回值为创建的对
象!
if(s3 == null){
s3 = new SingleDemo2();
}//和饿汉式的区别,此时才来创建对象!
return s3;
}
}
public class Demo14 {
public static void main(String[] args) {
SingleDemo s1 = SingleDemo.getS1();
SingleDemo s2 = SingleDemo.getS1();
SingleDemo2 s3 = SingleDemo2.getS3();
SingleDemo2 s4 = SingleDemo2.getS3();
System.out.println(s1 == s2);
System.out.println(s3 == s4);
}
}
输出:true true
备注:枚举更加安全些
package reviewDemo;
enum Stu{
jake;
//将构造方法私有化起来,反射也不能创建对象,安全
private Stu(){
}
}
public class Demo15 {
public static void main(String[] args) {
}
}
8、final 关键字
1 final 可以修饰类,方法,变量。
2 final 修饰类不可以被继承,但是可以继承其他类。
3 final 修饰的方法不可以被覆写,但可以覆写父类方法。
4 final 修饰的变量称为常量,这些变量只能赋值一次。
5 内部类在局部时,只可以访问被 final 修饰的局部变量。
6 final 修饰的引用类型变量,表示该变量的引用不能变,而不是该变量的值不能变;
Eg:
package reviewDemo;
final class Name{
}
class NewName extends Name{//ERROR,报错,因为Name有final修饰
}
public class Demo15 {
public static void main(String[] args) {
}
}
9、抽象类
当编写一个类时,我们往往会为该类定义一些方法,这些方法是用来描述该类的行为方式,
那么这些方法都有具体的方法体。
但是有的时候,某个父类只是知道子类应该包含怎么样的方法,但是无法准确知道子类如
何实现这些方法。
抽象方法的定义:通过 abstract 关键字来修饰的类称为抽象类;
我的总结:
抽象类用 private 修饰,里面可以有用 private 修饰的方法(没有方法体),强制子类进
行覆写;
可以理解为:具有某些公共方法的一个总结类。
可以定义被 abstract 修饰的抽象方法
抽象方法只有返回类型和方法签名,没有方法体。
备注:
抽象类可以含有普通方法
抽象类不能创建实例对象(不能 new)
需要子类覆盖掉所有的抽象方法后才可以创建子类对象,否则子类也必须作为抽象类
列举常见的几个抽象类:
流的四个基本父类
InputStream,OutputStream,Reader,Writer
我的总结:
抽象类是类的一种特殊情况:据有类的一切特点,但是不能实例化;一般的都得带有抽象
方法。
抽象类不可以实例化,有时看到的近似实例化是多态机制的体现,并不是真正的实例
化。
Eg:
Socket s = new Socket();
OutputStream os = s.getOutputStream();
左边是 OutputStream 类型变量的声明,右边是获取抽象类 OutputStream 的一个
实例对象!
package testDemo2;
abstract class Person{
}
class Student extends Person{
}
public class Demo2 {
public static void main(String[] args) {
Person p = new Student();//体现的是多态,父类声明实例化子类对象。
而不是抽象类实例化
}
}
abstract 方法
分析事物时,发现了共性内容,就出现向上抽取。会有这样一种特殊情况,就是功能声明相
同,但功能主体不同。
那么这时也可以抽取,但只抽取方法声明,不抽取方法主体。那么此方法就是一个抽象方法。
abstract [非 private 访问修饰符] 返回值类型 方法名称(参数列表);
抽象方法要存放在抽象类中。
抽象方法也可以存在于接口中
Eg:
package reviewDemo;
abstract class Person3{
abstract void show();
abstract void inof();
void turn(){
}
}
class NewP extends Person3{
@Override
void show() {
}
@Override
void inof() {
}
//不覆写的话会报错
}
public class Demo15 {
public static void main(String[] args) {
//new Person3();报错!因为抽象类不可以实例化
}
}
10、抽象类的体现-模板模式
抽象类是多个具体子类抽象出来的父类,具有高层次的抽象性;以该抽象类作为子类的模板可
以避免子类设计的随意性;
抽象类的体现主要就是模板模式设计,抽象类作为多个子类的通用模板,子类在抽象类的基础
上进行拓展,但是子类在总体上大致保留抽象类的行为方式;
编写一个抽象父类,该父类提供了多个子类的通用方法,并把一个或多个抽象方法留给子类去
实现,这就是模板设计模式;
模板模式应用的简单规则:
1.抽象父类可以只定义需要使用的某些方法,其余留给子类去实现;
2.父类提供的方法只是定义了一个通用算法,其实现必须依赖子类的辅助;
我的总结:
如果父类的方法不想被子类覆写,那么可以在前面加上 final 关键字修饰。
Eg:
package reviewDemo;
//模板模式
//抽象类中包含很多的抽象方法,子类必须去覆写!
abstract class Method{
abstract double mul();//返回值类型如果是void的话,下面报错,因为没有返
回值,无法引用!
abstract double divid();
void show(){
System.out.println("面积是:"+mul());//周长
System.out.println("面积是:"+divid());//面积
}
}
class Square extends Method{
double d;
public Square(double d) {
super();
this.d = d;
}
@Override
double mul() {
return d * d;
}
@Override
double divid() {
return 4 * d;
}
}
class Cirle extends Method{
double r;
public Cirle(double r) {
super();
this.r = r;
}
@Override
double mul() {
return 2 * 3.14 * r;
}
@Override
double divid() {
return 3.14 * r * r;
}
}
public class Demo16 {
public static void main(String[] args) {
Square s = new Square(5);
s.show();
Cirle c = new Cirle(4);
c.show();
}
}
11、接口(interface)
引入:抽象类是从多个类中抽象出来的模板,若要将这种抽象进行得更彻底,就得用到一种
特殊的“抽象类”→ 接口;
例子:
生活中听说过的 USB 接口其实并不是我们所看到的那些插槽,而是那些插槽所遵循的一种规
范;而我们看到的那些插槽是根据 USB 规范设计出来的实例而已,也就说插槽是 USB 的实例;
对应不同型号的 USB 设备而言,他们各自的 USB 插槽都需要遵循一个规范,遵守这个规范就
可以保证插入插槽的设备能与主板正常通信;
对于同一种型号的主板上的多个 USB 插槽,他们有相同的数据交换方式,相同的实现细节,可
认为他们都是同一个类的不同实例
我的总结:
接口只定义了类应当遵循的规范,却不关心这些类的内部数据和其方法内的实现细节.
接口只规定了这些类里必须提供的方法;从而分离了规范和实现.增强了系统的可拓展
性和维护性;
使用接口的好处,拓展性,维护性更好,所以我们在开发中会经常用到接口(. 相当于定义了
一种标准)
接口,类,对象示意图
![图片](https://i-blog.csdnimg.cn/blog_migrate/ec1dd33b21b791ecc746f9d276cd6e16.png)
interface 定义
接口定义一种规范,规定一个类必须做什么,但它不管如何具体去做;
[修饰符] interface 接口名 extends 父接口 1,父接口 2....
没有构造方法,不能实例化;
接口只能继承接口,不能继承类
接口里没有普通方法,方法全是抽象的;
接口里的方法默认修饰符是 public abstract;
接口里的字段全是全局常量,默认修饰符是 public static final;
接口里的成员包括(主要是前两个):
全局常量
公共的抽象方法
内部类(包括内部类,内部接口,内部枚举类);
我的总结:
接口没有构造方法,不能实例化!
接口里的方法全部是抽象的,没有普通方法,有默认的修饰符 public abstract,必须全部覆
写!
12、接口的使用
格式:public class SubImpl extends Super implements IA,IB
接口可以多继承,但是只能继承接口,不能继承类。
实现接口(支持多实现)
[修饰符] class 类名 implements 接口 1,接口 2...
接口的实现必须在 extends 之后;
实现接口的方法必须是 public 类型
接口不能创建实例,但是可以声明引用类型的变量。
此时,引用类型的变量必须指向到其实现类对象。
IStudent s = new String();//
IStudent s = new StudentImpl();//
接口与类之间的关系:
实现关系或者说是继承关系.
可以说类实现了接口的方法,也可以说类继承了接口的方法,不同情况下不同的理解!
13、面向接口编程之制定标准和简单工厂模式
制定一个标准,让别人去实现或者说满足它!
Eg:
interface USB{//定义 USB 标准
void useUSB();//USB 有使用 USB 的行为
}
简单工厂模式
构建一个工厂出来,在里面进行生产,用的时候直接拿
我的总结:
好处:屏蔽不同子类实现的差异,提高代码的可拓展性和可维护性;
package reviewDemo;
//简单工厂模式
interface Phone{//制定标准,都要实现send()方法
public void send();
}
class Iphone implements Phone{
@Override
public void send() {
System.out.println("Iphone手机在发短信");
}
}
class AndroidPhone implements Phone{
@Override
public void send() {
System.out.println("AndroidPhone手机在发短信");
}
}
class MyPhone implements Phone{
@Override
public void send() {
System.out.println("MyPhone手机在发短信");
}
}
class Factory{
public static void show(String type){//传入参数,根据不同的类型个性
化定制
if(type.equals("")){//为空的情况,不用往下执行
System.out.println("对不起,类型为空!,请重新输入!");
return;
}
Phone p = null;
if("Iphone".equals(type)){//判断类型
p = new Iphone();
}else if("AndroidPhone".equals(type)){
p = new AndroidPhone();
}else{
p = new MyPhone();
}
p.send();
}
}
public class FactoryDemo17 {
public static void main(String[] args) {
new Factory().show("Iphone");//调用方法
new Factory().show("AndroidPhone");
new Factory().show("MyPhone");
new Factory().show("YourPhone");
new Factory().show("");
}
}
输出:
Iphone手机在发短信
AndroidPhone手机在发短信
MyPhone手机在发短信
MyPhone手机在发短信
对不起,类型为空!
14、面向接口编程之适配器模式
使用一个现成的类,但是它的接口不完全符合你的需求,我只想要它其中的一个方法,不想
覆写其他的方法。
比如,窗体有变大,变小,关闭的行为,但是我现在只需要关闭行为;
package reviewDemo;
//适配器模式:只想用其中的某一个方法,用适配器作为中间的过渡
interface Windows{
void max();
void min();
void close();
}
//适配器模式,实现接口所有的方法,但是不写方法体!
class AdapterWindows implements Windows{
@Override
public void max() {
}
@Override
public void min() {
}
@Override
public void close() {
}
}
class MyWindows extends AdapterWindows{
//覆写父类的方法
public void close(){
System.out.println("这个实现的是关闭功能!");
}
}
public class Demo17 {
public static void main(String[] args) {
new MyWindows().close();
}
}
15、接口和抽象类的比较
相同点:
都位于继承的顶端,用于被其他实现或继承;
都不能实例化;
都包含抽象方法,其子类都必须覆写这些抽象方法;
区别:
抽象类为部分方法提供实现,避免子类重复实现这些方法,提供代码重用性;接口只能包
含抽象方法;
一个类只能继承一个直接父类(可能是抽象类),却可以实现多个接口;(接口弥补了 Java
的单继承)
二者的选用:
优先选用接口,尽量少用抽象类;
需要定义子类的行为,又要为子类提供共性功能时才选用抽象类;
我的总结:
接口不能有构造函数,抽象类是可以有构造函数的,
abstract 可以定义构造函数(包括带函数的构造函数),因为要保证其子类在创建的时
候能够进行正确的初始化,但是 Abstract 类不能被实例化。
知识点:如果不可以或者没有创建对象,那么我们必须加上 static 修饰,不能用对象调用,
就只好用类去调用。
![图片](https://i-blog.csdnimg.cn/blog_migrate/60be6b52b31781174363d584279c8179.png)
16、匿名内部类
适合只使用一次的类
不能是抽象类,因为系统在创建匿名内部类的时候,会立即创建匿名内部类的对象。
匿名内部类不能定义构造器,因为匿名内部类没有类名。
格式:
new 父类构造器([实参列表]) 或 接口()
{
//匿名内部类的类体部分
}
17、枚举类
使用 enum 声明,默认直接继承了 java.lang.Enum 类,而不是 Object 类;
枚举类的对象是固定的,实例个数有限,不可以再 new( ),枚举对象后可以跟()。
枚举元素必须位于枚举类体中的最开始部分,枚举元素后要有分号与其他成员分隔。
枚举类的构造方法的权限修饰符默认是 private;
一旦枚举对象后面加上{},那么该对象实际是枚举匿名内部类对象;
所有枚举类都提供一个静态的 values()方法(返回该枚举类所有对象组成的数组),便于遍历所
有枚举对象;
所有枚举类都提供一个静态的 valueOf(String name)方法, 返回枚举类中对象名等于 name 的
对象。
Eg:public enum Color{
RED(), GREEN(){}, BLUE{};
}
package reviewDemo;
//枚举
enum Color{
Green,Blue,Yellow;
@Override
public String toString() {
String ret = super.toString();
switch (this) {
case Green:
ret = "绿色";
break;
case Blue:
ret = "蓝色";
break;
case Yellow:
ret = "黄色";
break;
default:
break;
}
return ret;
}
}
class Personp{
Color c = Color.Blue;
void show(){
System.out.println(c);
}
}
public class Demo18 {
public static void main(String[] args) {
Color []color = Color.values();
for (Color c : color) {
System.out.println(c);
}
new Personp().show();
}
}
输出:
绿色
蓝色
黄色
蓝色
枚举类覆写接口抽象方法的两种方式:
在枚举类中实现接口的抽象方法;
在枚举匿名内部类中实现接口的抽象方法;
interface I{
void show();
}
enum Color implements I{
RED(){
public void show(){
}
}, GREEN{
public void show(){
}
}, BLUE{
public void show(){
}
};
}
enum Color implements I{
RED(), GREEN, BLUE;
public void show() {
}
}
我的总结:
枚举不可以 new();即便是反射也不可以!
备注:一个类如果没有构造方法,那么一定有相对应的某个方法可以获取对象!