Java入门学习——郝斌(四)

1. 内部类、匿名类

内部类

  • 内部类定义:在A类的内部但是所有方法的外部定义了一个B类,则B类就是A类的内部类,A是B的外部类
  • 内部类访问原则:
    • 内部类的方法可以访问外部类所有的成员
    • 外部类的方法不可以直接访问内部类的成员
//内部类的所有方法都可以访问外部类的所有成员,无论何种访问控制符,都可以,因为内部类被当作了外部类的一个成员
class A
{
	private int j = 10;
	private void f()
	{
		System.out.println("FFFF");
	}
	class B
	{
		private int k;
		private void g()
		{
			System.out.println("j = " + j); //内部类的私有方法可以访问外部类的私有属性
			f(); //内部类的私有方法可以访问外部类的私有方法
		}
	}
	public void m()
	{
		B bb = new B();
		bb.g();
	}
}
public class TestInnerClass
{
	public static void main(String[] args)
	{
		A aa = new A();
		aa.m();
	}
}
  • 内部类的优点:
    • 可以让一个类方便的访问另一个类中的所有成员
    • 增加程序的安全性,有效避免其他不相关类对该类的访问
  • 何时使用内部类:如果一个A类要使用B类的所有成员,并且A类不需要被除B类以外的其他类访问,则我们应当把A类定义为B类的内部类

匿名类

  • 匿名类是一种特殊的内部类
  • 如果在一个方法内部定义了一个匿名类,则该匿名类可以访问
    • 外部类的所有成员
    • 包裹该匿名类的方法中的所有final类型的局部变量
      • 注意:非final类型的局部变量无法被匿名类访问
import java.awt.*;
import java.awt.event.*;

public class TestWindow
{
	public static void main(String[] args)
	{
		//如果把10行的final去掉,本程序在18行就会报错,因为匿名类只能访问包裹它的方法中的final类型的局部变量
		final Frame f = new Frame(); //10行
		f.setSize(200, 200);
		f.addWindowListener(
			new WindowAdapter()
			{
				public void windowClosing(WindowEvent e)
				{
					f.setVisible(false); //18行
					System.exit(-1);
				}
			}
		);
		f.setVisible(true);
	}
}

创建匿名类的三种方式

  1. 继承父类
  2. 实现接口
  3. 实现抽象类
创建匿名类之实现接口
  • 假设A是接口名
  • 格式:
    new A()
    {
    	实现接口中方法的代码
    };
    
  • 功能:生成一个实现了A接口的匿名类
interface It
{
	void f();
	//void g(); // 如果该语句生效的话,21行到27行的代码中就必须得实现g方法
}
public class TestAnonyClass_1
{
	public static void main(String[] args)
	{
		It it = new It()
				{ //21行
					public void f()
					{
						System.out.println("hahah");
					}
				}; //27行
		it.f();
	}
}
创建匿名类之实现抽象类
  • 假设A是抽象类
  • 格式:
    new A()
    {
    	实现了A类的所有抽象类的方法代码
    	添加自己的方法或属性代码(不建议,因为没有实际意义)
    }
    
  • 功能:
    • 生成一个匿名类,该匿名类必须得实现了A类的所有抽象方法,当然该匿名类也可以定义自己的属性和方法
abstract class A
{
	abstract public void f();
	public void g()
	{
	}
}
public class TestAnonyClass_2
{
	public static void main(String[] args)
	{
		A aa = new A()
			{
				//f方法不可以被注释掉, 因为f是抽象方法,匿名类必须的实现
				public void f()
				{
					System.out.println("FFFF");
				}
				//g方法可以被注释掉
				public void g()
				{
					System.out.println("GGGG");
				}
			};
		aa.f(); //FFFF
		aa.g(); //GGGG
	}
}
创建匿名类之继承父类
  • 假设A是个类名
  • 格式:
    new A()
    {
    	重写了A类的方法代码
    	添加自己的属性和方法(不建议,因为没有实际意义)
    }
    
  • 功能:
    • 生成一个A类的子类对象,该匿名类对象继承了A的所有非private成员
class A
{
	public void f()
	{
		System.out.println("hhhh");
	}
}
public class TestAnonyClass_3
{
	public static void main(String[] args)
	{
		A aa = new A()
			{
				//重写了父类A的方法
				public void f()
				{
					System.out.println("嘿嘿");
				}
				//可以定义自己的方法,但没有任何实际意义
				public void g()
				{
					System.out.println("GGGG");
				}
			};
		aa.f(); //嘿嘿
		//aa.g();  // error
	}
}

匿名类的优缺点

  • 如果一个类的语句比较少,逻辑比较简单,而且不经常变动,这个时候可以使用匿名类
  • 如果一个类包含了很重要的逻辑,将来要经常修改,则这个类就不应该当作匿名类来使用,匿名类会导致代码的混乱

可运行jar包生成步骤

  1. 新建一个记事本文件,假设为1.txt,文件内容:
    • Main-class:可运行类的名字
    • 附注:记着敲回车
  2. dos下命令:
    • jar cvfm haha.jar 1.txt *.class
  • 记住:只有GUI程序生成的class文件才可以作为main class

2. 流

流概述

流的定义

流的定义

  • 流就是程序和设备之间嫁接起来的一根用于数据传输的管道,这个管道上有很多按钮,不同的按钮可以实现不同的功能
  • 这根管道就是流

流的示意图
流的示意图

输入时,程序在源(文件,网络,内存)上打开一个stream,然后如图一个一个顺序读。写也一样。

流的分类和使用

流的分类标准
  • java.io 包中定义了多个流类型(类或抽象类)来实现输入/输出功能;可以从不同的角度对其进行分类:
    • 按数据流的方向不同可以分为输入流和输出流
    • 按处理数据单位不同可以分为字节流和字符流
    • 按照功能不同可以分为节点流和处理流
  • J2SDK 所提供的所有流类型位于包java.io 内都分别继承自以下四种抽象流类型
分类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter
节点流与处理流

节点流,也叫原始流,为可以从一个特定的数据源(节点)读写数据(如:文件,内存)
节点流
处理流,也叫包裹六,是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能
处理流

四大基本流
四大基本抽象流
  • InputStreamOutputStream读写数据的单位是一个字节

  • ReaderWriter读写数据的单位是一个字符

  • 在Java中一个字符占两个字节

  • InputStream OutputStream Reader Writer 这四个类都是抽象类

  • 凡是以stream结尾的都是字节流

InputStream 流中常用的方法
  • public int read() throws IOException
    • 读取一个字节并以整数形式返回
    • 如果读取到输入流的末尾则返回-1
  • public int read(byte[] b) throws IOException
    • 从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中。以整数形式返回实际读取的字节数
    • 如果b的长度为0,则不读取任何字节并返回0;如果因为流位于文件末尾而没有可用的字节,则返回值-1
    • 例子:
      FileInputStream fis = new FileInputStream("d:\\errorlog.txt");
      len = fis.read(buf); //从fis流所关联的 d:\\errorlog.txt 文件中读取数据,并将读取出来的数据写入buf数组中,
      				//返回值是实际写入 buf 数组的字节个数,如果读取到问家的结尾,则返回-1
      
  • public int read(byte[] b, int off, int len) throws IOException
    • 从输入流中最多读取len个字节的数据并存入byte数组中
    • b表示读取的数据要存入的数组的名字
    • off表示第一个读出的数据要存入的位置,是下标
    • len表示最多能读取的字节数
    • 将从输入流所关联到的设备中读取的第一个字节存储在元素b[off]中,下一个字节存储在b[off+1]中,依次类推。读取的字节数最多等于len
    • 尝试读取len个字节,但读取的字节也可能小于该值,以整数形式返回实际读取的字节数
    • 如果读到了文件的末尾,则返回-1
  • void close() throws IOException
    • 关闭此输入流并释放与该流关联的所有系统资源
  • long skip(long n) throws IOException
    • 跳过和丢弃此输入流中数据的n个字节。这个用的很少
OutputStream 流中常用的方法

向输出流中写入一个字节数据,该字节数据为参数b的低8位
void write(int b) throws IOException

将一个字节类型的数组中的数据写入输出流
void write(byte[] b) throws IOException

将一个字节类型的数组中的从指定位置(off)开始的len个字节写入到输出流
void write(byte[] b, int off, int len) throws IOException

关闭流释放内存资源
void close() throws IOException

将输出流中缓冲的数据全部写出到目的地
void flush() throws IOException

Reader 流中常用的方法

读取一个字符并以整数的形式返回(0~255),如果返回-1已到输入流的末尾
int read() throws IOException

读取一系列字符并存储到一个数组buffer,返回实际读取的字符数,如果读取前已到输入流的末尾返回-1
int read(char[] cbuf) throws IOException

最多读取length个字符,并存储到一个数组buffer,从length位置开始,返回实际读取的字符数,如果读取前已到输入流的末尾返回-1
int read(char[] cbuf, int offset, int length) throws IOException

关闭流释放内存资源
void close() throws IOException

跳过n个字符不读,返回实际跳过的字节数
long skip(long n) throws IOException

Writer 流中常用的方法

向输出流中写入一个字符数据,该字节数据为参数b的低16位
void write(int c) throws IOException

将一个字符类型的数组中的数据写入输出流
void write(char[] cbuf) throws IOException

将一个字符类型的数组中的从指定位置(offset)开始的length个字符写入到输出流
void write(char[] cbuf, int offset, int length) throws IOException

将一个字符串中的字符写入到输出流
void write(String string) throws IOException

将一个字符串从offset开始的length个字符写入到输出流
void write(String string, int offset, int length) throws IOException

关闭流释放内存资源
void close() throws IOException

将输出流中缓冲的数据全部写出到目的地
void flush() throws IOException

文件流

  • 文件流包括
    • FileInputStream FileOutputStream ——字节流
    • FileReader FileWriter ——字符流
  • 例子:读取一个文件的内容并将其输出到显示器上,并统计读取出来的字节的个数
import java.io.*;
public class TestFileReader
{
	public static void main(String[] args)
	{
		FileReader fr = null;
		try{
			fr = new FileReader("D:\\Haobinlearning\\JavaSE\\P89-96\\TestFileReader.java");
			int cnt = 0;
			int ch;
			ch = fr.read();
			while (-1 != ch)
			{
				System.out.print((char)ch);
				ch = fr.read();
				++cnt;
			}
			System.out.printf("总共从TestFileReader.java文件中读取了%d个字符", cnt);
		}
		catch (IOException e){ }
	}
}
FileInputStream的使用
  • InputStream是用来读取字节的,是个抽象类,我们通常使用的是该类的子类
  • FileInputStreamInputStream 的子类,利用 FileInputStream 可以将一个文件的内容按字节为单位读取出来
  • FileInputStream 有一个很常用的构造函数
    • public FileInputStream(String fileName) throws FileNotFoundException
    • 利用该构造函数可以实现将输入流连接到某个文件的功能
    • 必须对本构造函数抛出的异常进行捕捉
    • 如果用字符串来表示操作系统的文件路径时,我们可以使用\\/ 两种方式来作为文件夹的路径分隔符
  • FileOutputStream 同理
FileReader 的使用
  • Reader 是用来读取字符的,是个抽象类,我们通常使用的是该类的子类
  • FileReaderReader的子类,利用FileReader 可以将一个文件的内容以字符为单位读取出来
  • FileReader 有一个很常用的构造函数
    • public FileReader(String fileName) throws FileNotFoundException
    • 利用该构造函数可以实现将输入流连接到某个文件的功能
    • 必须对本构造函数抛出的异常进行捕捉
    • 如果用字符串来表示操作系统的文件路径时,我们可以使用\\/ 两种方式来作为文件夹的路径分隔符
  • FileWriter 同理
字节流与字符流的区别
  • FileInputStreamFileOutputStream 可以完成所有格式文件的赋值
  • FileReaderFileWriter 只可以完成文本文件的复制,却无法完成视频格式文件的复制
  • 因为字节是不需要解码和编码的,将字节转化为字符才存在解码和编码的问题
  • 字节流可以从所有格式的设备中读写数据,但字符流只能从文本格式的设备中读写数据
    以下程序实现文件的复制
import java.io.*;
public class TestFileInputStreamOutputStreamCopy
{
	public static void main(String[] args)
	{
		FileInputStream fi = null;
		FileOutputStream fo = null;
		try
		{
			fi = new FileInputStream("D:\\JavaSE\\P89-96\\妹妹来看我.mp3");
			fo = new FileOutputStream("D:\\JavaSE\\P89-96\\Output1.txt");
			int ch = fi.read();
			while (-1 != ch)
			{
				fo.write(ch);
				ch = fi.read();
			}
			fi.close();
			fo.close();
		}
		catch (Exception e){
		}
	}
}
import java.io.*;
public class TestFileReaderWriterCopy
{
	public static void main(String[] args)
	{
		FileReader fr = null;
		FileWriter fw = null;
		try
		{
			fr = new FileReader("D:\\JavaSE\\P89-96\\妹妹来看我.mp3");
			fw = new FileWriter("D:\\JavaSE\\P89-96\\Output2.txt");
			int ch = fr.read();
			while (-1 != ch)
			{
				fw.write(ch);
				ch = fr.read();
			}
			fr.close();
			fw.close();
		}
		catch (Exception e){
		}
	}
}

缓冲流

缓冲流概述
  • 缓冲流就是带有缓冲区的输入输出流
  • 缓冲流可以显著的减少我们对IO访问的次数,保护我们的硬盘
  • 缓冲流本身就是处理流(处理流也叫包裹流),缓冲流必须得依附于节点流(节点流也叫原始流)
  • 处理流是包裹在原始节点流上的流,相当于包括在管道上的管道
缓冲流
  • 缓冲流要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写的效率,同时增加了一些新的方法
  • J2SDK提供了四种缓冲流,其常用的构造方法为:
    • BufferedReader(Reader in)
    • BufferedReader(Reader in, int sz) // sz 为自定义缓存区的大小
    • BufferedWriter(Writer out)
    • BufferedWriter(Writer out, int sz)
    • BufferedInputStream(InputStream in)
    • BufferedInputStream(InputStream in, int size)
    • BufferedOutputStream(OutputStream out)
    • BufferedOutputStream(OutputStream out, int size)
  • 缓冲输入流支持其父类的markreset方法
  • BufferedReader提供了readLine方法用于读取一行字符串(以\r\n 分隔)
  • BufferedWriter 提供了newLine 用于写入一个行分隔符
  • 对于输出的缓冲流,写出的数据会先在内存中缓存,使用flush方法将会使内存中的数据立刻写出
BufferedOutputStream 和 BufferedInputStream
  • BufferedOutputStream:带缓冲的输出流,允许一次向硬盘写入多个字节的数据
  • BufferedInputStream:带缓冲的输入流,允许一次向程序中读入多个字节的数据
  • BufferedOutputStreamBufferedInputStream 都是包裹流,必须得依附于OutputStreamInputStream
  • 例子:利用BufferedOutputStreamBufferedInputStream完成大容量文件的复制,这远比单纯利用FileInputStreamFileOutputStream 要快得多

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\CD4.rmvb")); //bis输入流有个默认的缓冲区,大小为32个字节
byte[] buf = new byte[1024];
int len = bis.read(buf, 0, 1024);

一定要注意,bis.read(buf, 0, 1024); 这不是从buf中读数据,而是从bis所关联到的"D:\\CD4.rmvb"文件中读取数据,并将读取的数据写入bis自己的默认缓冲区中,然后再将缓冲区的内容写入buf数组中,每次最多向buf数组中写入1024个字节,返回实际写入buf数组的字节个数,如果读到了文件的末尾,无法再向buf数组中写入数据,则返回-1

  • BufferedInputStream 流中有public int read(byte[] b)方法用来把从当前流关联到的设备中读取出来的数据存入一个byte数组中
  • BufferedOutputStream 流中有 public int write(byte[] b) 方法用来把byte数组中的数据输出到当前流所关联到的设备中
  • 如果我们希望用BufferedInputStreamBufferedOutputStream 完成“将一个设备中的数据导入另一个设备中”,我们就应该定义一个临时的byte类型的数组,用这个临时数组作为输入流与输出流进行交互的中转枢纽
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值