public abstract void hello();
通过抽象类,可以避免子类设计的随意性。通过抽象类,我们就可以做到严格限制子类的的设计,使子类之间更加通用。(程序的可拓展和可维护性)
Animal 动物类->设计为抽象类,定义一些抽象方法如sbstract void shout();方法
//必须使用default修饰
public default void method(){
}
public class A implements MyInterface{
}
public class A extends B implements C{
}
如果接口B继承了接口A,而接口中有一个抽象方法method();那么B的一个实现将拥有且必须实现这个抽闲方法method()
public interface InterfaceA{
public abstract void method();
}
interface InterfaceB extends InterfaceA{
}
class Iml implements InterfaceB{
@Override
public abstract void method{
//TODO balabalabala
}
}
一个类可以继承多个接口,格式如下
class ClassExample implements InterfaceA,InterfaceB{ //多个接口之间使用逗号分开
}
举例:飞机,鸟,炮弹,宇宙飞船这几种物体
继承:is-a 关系,三角形is a 几何图形(实例)
接口:has-a 关系 鸟has a飞行功能,飞机has a 飞行功能,我们就可以用接口的方式来定义“飞”这种能力,让其他类去实现
//接口体现的是一种能力
1.编写接口
2.实现类实现接口中的方法(实现方法不加abstract)
3.接口(类型)new 实现类对象
这就是面向接口的编程
public interface Fly{
void flying();
}
class Bird implements Fly{
@override
public void flying{
System.out.println("小鸟在飞");
}
}
class Plane implements Fly{
@override
public void flying{
System.out.println("飞机在飞");
}
}
class Dog implements Fly{
@override
public void flying{
System.out.println("小狗被踢飞");
}
}
//以下代码在一个测试类中
public static void main(String args[]){
Fly bird = new Bird;
bird.flying();
Fly puppy =new Dog;
puppy.flying();
Fly airPlane =new Plane();
airPlane.flying();
}
public class Face{
private String shape="瓜子脸";
public class Nose{
private String shape="高鼻梁";
public void breath(){
System.out.println("鼻子在呼吸");
}
}
//测试类
public void static main(String args[]){
Face f = new Face();
Nose n = f.new Nose();
n.breath();
}
}
外部类名.内部类名 //对!就是这样的
当要实例一个静态内部类的对象时:
外部类名.内部类名 实例名 = new 外部内名.内部类名(); //就是这样子
外部类名.this.成员变量
定义在方法中的内部能,只能在方法中去使用,如下
public void Hello(){
int a=10;
class Inner{
public void kk(){
System.out.println("a="+a);
}
}
new Inner().kk();//只能在方法体中调用方法中的内部类,对于方法外,这个内部类毫无意义
}
//以匿名继承内部类举例
public abstract class Father{
public abstract void dream() ;
}
class Test{
public static void main(String[] args){
Father f = new Father(){ //这个部分就是匿名内部类
@Override
public void dream() {
System.out.println("欧耶实现了父亲的梦想!");
}
};
f.dream();//调用内部内的方法
}
}
String str = "abc";
str = "def"; //在这里字符串并没有被改变,改变的只是指向它的地址
//在常量池中,有两个字符串常量"abc" 与"def",刚开始时前者的地址指向str,执行第二句后,变成了后者的地址指向str
//使用方法
字符串对象.charAt();
//使用方法
字符串对象.equalsIgnoreCase(<这里输出要比较的字符串,不区分大小写>) // 返回布尔型
//不带参数的使用方法
字符串对象.indexOf(<需要找的字符串或字符>) //返回该字符或字符串在字符串中的位置(正向查找,从0开始)int型
字符串对象.lastIndexOf(<需要找的字符串或字符>) //正向查找最后一个符合要求的字符或字符串
//带参数的使用方法
字符串对象.indexOf(<需要查找的字符串或字符><,index>) //从index位置正向查找最近的一个符合要求的结果,返回位置
字符串对象.lastIndexOf(<需要查找的字符串或字符><,index>) //从index位置方向反向查找最近的符合要求的值,返回位置
字符串对象.length() //括号里无需添加参数
字符串对象.replace()
//获得并返回一个替换后的新字符串,但并没有改变原字符串(因为字符串只能只能共享,不能改变)
API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。
//无法上传,自定查看eclipse源码
char [] c={'a','b','c'}
String str =new String(c);
String s1 ="abc";
String s2="a"+"b"+"c";
String s3=new String("abc");
String s4 =s3+"";
String s5= new Stirng("abc");
System.out.println(“s1==s2:”+(s1==s2)); //结果true 因为字符串已经在堆中常量池加载了,不需要开辟空间,所以s1和s2都指向常量池“abc”的地址
System.out.println(“s1==s3:”+(s1==s23); //结果false 因为使用了new,在堆中开辟了空间,s3指向的是堆中的空间的地址
System.out.println(“s1==s4:”+(s1==s4)); //结果false 变量运算时也要在堆中开辟一个空间,在此空间中运算,此空间地址指向s4
System.out.println(“s1==s5:”+(s1==s5)); //结果false 同样是因为使用了new开辟了一个空间
//equals原理:比较两个对象地址是否相同,比较两个字符串是否相同
public boolean equals(Object anObject) { //传入比较的对象
if (this == anObject) { //如果当前对象和比较对象地址相等,返回true
return true;
}
if (anObject instanceof String) { //如果传入的对象不是String的一个实例,返回false,否则往下再判断
String anotherString = (String)anObject; //传入对象向下转型为String
int n = value.length;
if (n == anotherString.value.length) { //如果两个字符串的长度不相等,返回false,否则往下判断
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false; //如果有一个字符不相等,则返回false
i++;
}
return true; //字符全部相等则返回true
}
}
return false;
}
使用二维数组来讲解,二维数组就是用来存储一维数组的数组
//这里用int型举例
int[][] arrA = new int[index][] //其中index为二位数组的长度
//静态初始化
int [][] arryA = {{1,2},{3,4,8},{5,6,7}} //二维数组是不规则的矩阵,它包含的各一维数组可以不相同
//声明了一个二维数组,用于存储3个一维数组,每个一维数组的长度未定
int[][] arrA = new int[3][]
arrA[0]=new int[3];//声明arrA[0]的长度为3,初始化为0(详见各种数据类型的默认初始化值)
arrA[1]=new int[]{1,2,3};//直接给定该一维数组的元素
arrA[2]=new int[3];
//声明一个二维数组,且给出所有一位数组的长度
int[][] arryB = new int[3][3];
for(<数据类型> <迭代变量> : 数组名){
System.out.println(<迭代变量>);
}
for(int[] arr : arrA){
for(int a : arr){ //arr和上一行的arr是同一个数组(一维数组),a是一个整型迭代变量
System.out.println(a);
}
}
数组除了可以存储基本数据类型,还可以存储引用数据类型。
这里举个例子,我们使用一个数组来存储一组对象
//在Person类中
public class Person {
private String name;
private String gender;
private int age;
public Person(String name, String gender, int age) {
super();
this.name = name;
this.gender = gender;
this.age = age;
}
public Person() {
super();
}
@Override
public String toString() {
// TODO Auto-generated method stub
return name+"\t"+gender+"\t"+age;
}
}
//在Test测试类中
public class Test {
public static void main(String[] args) {
Person p[] =new Person[3];
p[0]=new Person("张三", "男", 32);
p[1]=new Person("李四","男",21);
p[2]=new Person("王二麻子","女",23);
for (int i = 0; i < p.length; i++) {
System.out.println(p[i]);
}
}
}
数组A=数组B;//将数组B对应的内存空间的地址指向数组A,执行这一句后,数组A与数组B的地址相同,它们完全相等
System.arraycopy(<数组a>,index1,<数组b>,index2,index3) //其中index1表示从数组a的index1位置开始复制,复制到数组b从index2开始的位置,复制index3个
Arrays.toString(数组)
Arrays.equals(数组a,数组b) //比较两个数组以相同的顺序包含相同的元素
//举例:将数组a的元素赋值给b,内存中奖开辟新的空间,将地址指向b
int[] a = {1,2,3,4,5};
int[] b=new int[6];
b=Arrays.copyOf(a, 8); //这里的8表示新数组的长度为8,a数组填不满的用0来填
int [] arrA =new int [4];
Arrays.fill(arrA,9) // 表示arrA中的每个元素都填成9
Arrays.sort(arrA)将数组arrA进行升序排序(注意是升序排序,不是排序后输出)
一个一维数组有N个元素,进行N-1轮比较,每轮使一个元素和下一个相邻的元素进行比较,将大的数交换到右边
public class Test {
public static void main(String[] args) {
int[] a = {11,52,23,4,56};
System.out.println(Arrays.toString(a));
for (int i = 0; i < a.length-1; i++) { //4轮比较
for (int j = 0; j < a.length-1; j++) { //每轮比较4次
int temp; //为了优化算法,temp最好定义在循环外
if (a[j]>a[j+1]) {
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
System.out.println(Arrays.toString(a));
}
}
通过观察,我们发现没比较一轮,就可以少比较一个数,所以有一下优化算法
public class Test {
public static void main(String[] args) {
int[] a = {11,52,23,4,56};
int temp;
System.out.println(Arrays.toString(a));
for (int i = 0; i < a.length-1; i++) { //4轮比较
for (int j = 0; j < a.length-1-i; j++) { //每轮少比较i次
temp;
if (a[j]>a[j+1]) {
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
System.out.println(Arrays.toString(a));
}
}
折半查找法只适用于已经正序升序的数组,通过不断取数组中间值来与需要查找的元素进行比较,成功则返回该元素的值,失败则返回负的当前最低值-1
此方法相当于arrays.binarySearch()方法
package 测试专用;
import java.util.Arrays;
import 测试专用.Face.Nose;
public class Test {
public static void main(String[] args) {
int[] a = {11,22,33,44,55,66};
int high=a.length-1;
int low =0;
int va=66;//需要查找的值
boolean flag =false;
while(low<=high) {
int mid=(low+high)/2;
if (a[mid]>va) {
high=mid-1;
}else if (a[mid]
异常(Exception)就是在程序运行过程中所发生的不正常的事件,它会中断正在运行中的程序。
当java程序出现以上异常时,就会在所处的方法中产生一个异常对象,这个异常对象包括异常的类型,异常出现时程序的运行状态以及对该异常的详细描述。
Error相当于人类当中的癌症,仅靠程序本身是无法恢复的严重错误,它与异常同属一个父类Throwable
异常是由java应用程序抛出和处理的非严重错误,异常相当于人类中的小病症
例如:SQLException、ClassNotFoundException等等
例如:ArithmeticException、NullPointerException、NumberFormatException等等
通过捕获异常的方式,来处理相对应的异常,让程序能够继续执行下去。
try {
需要捕获的代码体
} catch (Exception e) { //括号内为捕获的异常类型,Exception为所有异常类的父类,这里是父类引用指向子类对象,也可以写具体的异常在里面如 InputMismatchException
System.err.println("成功捕获后相对应的执行代码区"); //在异常中使用err.println输出,文字为红色
}
finally下的代码块内的代码无论如何都会被执行,即便没有报异常
try{
需要捕获的代码块
}finally{
无论如何都会执行的代码块,即使没有报异常
}
包含了catch和finally的功能,此种组合catch与finally位置不能互换
try{
需要捕获异常的代码块
}catch{
catch代码块
}finally{
finally代码块
}
注:如需要try下的代码块中有return,先执行finally下的代码块再回来执行return
该关键字用在方法名的后面,用于声明该方法可能会产生一个异常,如果方法声明的是Exception类型的异常或者是Checked Exception异常,要求方法的调用出必须做处理,RuntimeException类型的异常无预处理。处理方式有以下两种:
public static void method() throws <异常类名>{ //异常类名笼统的可以写Exception,细致的可以写具体哪个异常类,但只有Exception类型与Checked类型的异常需要做处理
}
public static void main(String[] args) <相同的异常类名>{
method(); //当这里调用method方法时main方法名后就要添上相同的异常类名,否则使用下一种方法解决
}
public static void method(){
}
public static void main(String[] args){
try{
method(); //需要捕获异常的方法
}catch(<异常类名> e){
}
}
public class Father {
public void method() throws Exception { //父类的method方法声明了一个异常类型Exception
}
}
class Son extends Father{
public void method() <此处可声明可不声明异常> {
super.method(); //但这里使用super调用了父类的方法,则一定要进行处理
}
public static void main(String[] args) {
}
}
在捕获一个异常之前,必须有一段代码先生成异常对象并把它抛出,这个过程我们以手工做,也可以由JRE来实现,但是他们调用的都是throw字句。手动调用如下:
public static void method() {
try {
throw new Exception(); //这行代码不写时由JRE来自动实现,new Exception为Exception的一个对象空间
} catch (Exception e) { 声明的这个e指向上面new Exception开辟的空间
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(<除数>!=0){ //如果除数不为0,则继续向下执行
}
if(<对象>!=null){ //如果对象不为空,则往下执行
}
if(<对象a> instanceof <类型B>){ //如果对象a是类型B或类型B的子类的一个实例,则程序向下继续执行
}
public class Father {
public static void main(String[] args) {
int a = 0 ;
Scanner scan =new Scanner(System.in);
if (scan.hasNextInt()) { //这里使用hasNextInt()来判断输入的值是否为int型,是则返回true
a=scan.nextLine(); //除hasNextInt()之外还有hasNextByte()、hasNextLine()方法等,便于灵活运用
}
System.out.println(a);
}
}
Checked类异常有很多种,处理的方法为向上声明和实现try-cathc异常捕获,实现规则请笔记向上翻阅
在程序中,可能会遇到任何标准异常类都没有充分的描述清楚问题(比如某次输入你只允许输入1到100之间的数),这种情况下可以创建自己的异常类。
自定义异常类必须手写throw抛出异常对象
//自定义异常类
public class SexException extends Exception{
public SexException() { //构造一个无参构造器
super();
// TODO Auto-generated constructor stub
}
public SexException(String message) { //构造一个String类型的构造器,用来返回自定义字符串
super(message);
// TODO Auto-generated constructor stub
}
}
public class TestSexException {
public static void main(String[] args) {
System.out.println("请输入性别:");
Scanner scan = new Scanner(System.in);
String gender =scan.nextLine();
if ("男".equals(gender)||"女".equals(gender)) {
System.out.println("你的性别是:"+gender);
}else {
try {
throw new SexException("性别输入错误"); //由于是自定义异常类,这里Jre无法实现抛出对象,必须手写throw抛出异常对象
} catch (SexException e) {
e.printStackTrace();
}
}
}
}
java语言是一个面向对象的语言,但是java中的基本数据类型却不是面向对象的。而我们在实际使用中经常需要将基本数据转化为对象,便于操作。比如:集合的操作中,我们就需要将基本类型数据转化为对象。
基本数据类型 | 包装类 |
---|---|
byte | Byte |
boolean | Boolean |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
graph BT
Character-->Object
Number-->Object
Boolean-->Object
Byte-->Number
Short-->Number
Integer-->Number
Float-->Number
Double-->Number
这里我们使用Integer类举例
Integer i1 =new Integer(23); //定义一个Integer类的对象,值为23
int i = i1.intValue();
Integer i4 = Integer.valueOf(123);//将基本数据类型123转为对象那个i4
int i2 =Integer.parseInt("234");//将一个纯数字的字符串转为一个int型数据
int i3 =123;
String s =String.valueOf(i3); //方法1
String s = i3+""; //方法2
Integer i5 = new Integer(34);
String ss = i5.toString();
自动装箱和自动拆箱提供了基本数据类型与它相同类型的包装对象之间的转换,目的是减少工作量
基本数据类型就自动的封装到和它相同类型的包装类当中去,这个过程是编译器帮我们做的
//栗子
Integer a =100; //此代码编译不会报错,这就是自动装箱
这行代码它实际上是调用了valueOf(int i)方法
关于valueof()方法,它会调用一个IntegerCache类,这是一个Integer当中的内部类,它做一个缓存功能
当你获取的值在-128到127之间时,地址都指向这个缓存池,所以
Integer b=100;
(a==b)将返回true;
包装类对象自动转换为基本数据类型
//同样使用Integer与int型举例子
Integer a = new Integer(100);
int b = a; //对象直接赋值给了基本数据类型b
这是因为编译器替我们做了转型的工作,他实际上调用了
intValue()方法
本节的内容详见2.1.5
String的底层数据结构是Char类型的数组
String的相应方法的实现实际上就是对数组的一个操作
StringBuilder:效率高,安全性低
StringBuffer:效率低,安全性高
StringBuilder 是一个可变的字符序列。它继承于AbstractStringBuilder,实现了CharSequence接口。StringBuilder和StringBuffer是通用的,它们区别大多在安全与效率上。
StringBuilder a= new StringBuilder();
a.append(false);
a.append("hello");
a.append(193);
a.append('你');
System.out.println(a); //将输出:falsehello193你
StringBuilder a= new StringBuilder();
Integer b=300;
a.insert(0,b) //此时字符串a为300
一段代码引发的知识点
String st1 ="abc"; --1
String st2 =st1+"def"; --2
System.out.println(st2); --3 //则输出abcdef
我们讲讲第2行代码是怎么实现的:
前面说过在有new或变量运算时会在堆中开空间,用来作计算,实际上,开的这个空间其实是StringBuilder的一个对象对应的空间,第2行代码可看作:
StringBUilder sb = new StringBUilder();
sb.append(st1);
sb.append("def");
st2=sb.toString();
本节内容需要提到的知识点,箭头代表继承
graph LR
java.sql.Date-->java.util.Date
java.sql.TimeStamp-->java.util.Date
java.sql.Time-->java.util.Date
这两个类完成字符串和时间对象的转换,其中java.util.DateFormat类为抽象类
graph LR
java.text.SimpleDateFormat-->java.text.DateFormat
在本节中我们只学习将String类型的数据转化为日期时间与将时间日期转化为String型数据
public static void main(String[] args) {
Date d =new Date(1264561654654L);
DateFormat d2 =new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");//有关括号内参数的表示请务必参考JDK8的API
String str=d2.format(d);
System.out.println(str); //输出结果为:2010-01-27 11:07:34.654
}
DateFormat d2 =new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
String str="yyyy-MM-dd hh:mm:ss.SSS";//括号内为格式,需要填写具体数值
Date d=d2.parse(str);
System.out.println(d);//此时执行的是Date型对象d的toString方法
graph LR
java.util.GregorianCalendar-->java.util.Calendar
set(Calendar.DAY_OF_MONTH,1)初始化当前的“日”为当月的第一天
Math类是用来提供数学的一些基本运算方法与常量
import xxxx 和 import static xxxx的区别是前者一般导入的是类文件如import java.util.Scanner;后者一般是导入静态的方法,import static java.lang.System.out;//这里导入的是out方法
FIle类是文件和目录路径名的抽象表示形式。一个File对象可以代表一个文件或目录。可以实现获取文件和目录属性等功能。可以是实现对文件和目录的创建、删除等功能。File不能访问文件内容。
//FIle类没有无参构造方法,在new 的时候必须传入参数,如:
File f = new File("a.txt"); //在当前项目目录下预创建一个a.txt的文件
File f2 =new File("D://a.txt"); 地址分隔符可以为\或/
File f3 =new File("D://B");没有跟后缀名的文件
File f4 =new File(f3,"a.txt"); //在f3的前提(如果f3是一个存在的文件夹)下创建一个文件a.txt
File f5 =new File("D:"+File.separator+"a.txt"); //由于unix系统的文件路径分割符与windows不同,File.separator会自动匹配不同系统的分隔符
此方法返回的是一个boolean类型的值,当且仅当具有对象文件名的文件不存在时,创建一个新的空白文件
f1.createNewFile()
f1.delete()
注:无论创建还是删除都是对是预创建的文件进行操作
//方法同声明文件,这里只举一个例子
File f = new File("D:\\hello") --1 //预创建一个文件或文件夹
File f2 = new File("D:\\aa\\bb\\cc") --2 //这种实际上是预创建一个名为cc的文件夹或文件
//输出E盘下的所有文件及文件夹
File f1 =new File("E:\\");
String[] str =f1.list(); //使用String数组接收
for(String s : str) { //遍历
System.out.println(s);
}
File f1 =new File("E:\\");
File[] fileList =f1.listFiles(); //使用File数组接收
for(File s : fileList) { //遍历
System.out.println(s);
}
File f =new File("D:\\abc");
f.createNewFile();
f.mkdir();
在此段代码执行后,将只能生成一个名为abc的文件,而不能生成文件夹,如果2行与3行代码互换,则只能生成文件夹而不能生成文件。
若该地址原本就有名为abc的文件或是文件夹,则无法再生成文件或是文件夹
public class TestRecursionFile {
public static void main(String[] args) {
// TODO Auto-generated method stub
File file =new File("D:\\Test");
printFile(file,0);
}
public static void printFile(File file, int level) { //递归方法体
File f = file;
int l = level; //level用来表示层次,输出符号“-”
for (int i = 0; i < level; i++) {
System.out.print("- ");
}
System.out.println(f.getName()); //输出当前文件名
if (f.isDirectory()) { //如果当前对象是文件夹,则:
File[] fileList = f.listFiles(); //列举子文件对象
for (File temp : fileList) {
printFile(temp, level + 1); //子文件调用printFile方法
}
}
}
}
枚举与类同级,但它实质上也是类,枚举用来让用户做特定值的选择,如输入性别时,你只能选择“男”或者“女”,而不能选择其它的。
枚举和类以及接口处于同一级别,使用关键字enum来声明而不是class新建枚举的方法为:new-Enum
public enum Gender{
//如此
}
枚举的成员即我们前面说的用户所选的“特定值”,他有如下特征:
public enum Gender{
男,女;
}
public enum Gender{
男,女;
int a;
String name="李白";
public void printHello(){
System.out.println("Hello");
}
}
枚举的属性可以在其它类中调用
//数据来源上面代码
Gender g =Gender.男;
Gender f =Gender.女;
枚举中可以写方法,当需要调用时,使用枚举成员的实例来调用
//每个被枚举的成员实质上就是一个对象
如://接着使用上面的Gender枚举
public class Test{
public static void main(String[] args){
Gender g =Gender.男;
g.printHello(); //此时g就相当于一个对象
String name =g.name;
}
}
//枚举成员转String
String gender=g.toString(); //g从上面来的
///String 转枚举成员
//String类型转枚举
Enum g = Enum.valuOf(Enum.class,"人妖");
package cn.sxt.test;
public class Person {
private String name;
private int age;
private Gender gender; //声明一个枚举对象
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Gender getGender() {
return gender;
}
public void setGender(Gender gender) {
this.gender = gender;
}
public Person(String name, int age, Gender gender) {
super();
this.name = name;
this.age = age;
this.gender = gender;
}
public Person() {
super();
}
}
package cn.sxt.test;
public class Test { //这里使用测试类Test来测试
public static void main(String[] args) {
Person p1 = new Person("tom",32,Gender.女);
switch(p1.getGender()) {
case 女:
System.out.println("我是女人");
break;
case 男:
System.out.println("我是男人");
break;
}
}
}
容器分很多种,List接口下的ArrayList,LinkedList,Vector等,Set接口下有HashSet,TreeSet等。
graph TD
List接口-->Collection接口
Set接口-->Collection接口
ArrayList类-->List接口
LinkedList类-->List接口
Vector类-->List接口
HashSet类-->Set接口
TreeSet类-->Set接口
java集合框架提供了一套性能优良、使用方便的借口和类,他们位于java.util包中,存放在集合中的数据,被称为元素(element)。他是所有容器的顶层。方法具体详情请查看jdk 1.8 API:
list.add(new Scanner(System.in));
list.add(100);//100自动装箱为Integer类的一个对象
size()方法:返回容器中元素的个数,注意是元素的个数而不是字符数
isEmpty()方法:返回一个布尔值,如果集合是空的则为true
addAll(集合)方法:将指定集合中的元素全部添加到当前集合中
remove(Object)方法:根据对象来删除
list.remove(new Integer(100)); //将上面添加的值为100的Integer的对象删除
removeAll(集合A)方法:将当前集合中的集合A所包含的所有元素的元素删去
contains(元素)方法:判断元素在当前集合中是否存在
直接输出List对象将输出集合中的元素
clear()方法:删除当前集合中的所有元素
for(Object obj : list){
System.out.println(obj)
}
for(int i=0;i
Iterator ite = list.iterator;
while(ite.has.hasNext()){
Object obj =ite.next();
System.out.println(obj);
}
迭代器是用来去除集合中元素的工具。每种容器的数据结构不同,所以他们的取出元素的方法也不同,但他们都有判断和取出的过程,于是我们将取出器(用来调用取出方法)抽象出来,得到一个抽象接口Iterator,每种容器中都有一个内部类,用来实现Iterator,这个类的对象,就是容器的迭代器。
ArrayList a;
Iterator temp = a.iterator();
//hasNext()方法是用来判断集合是否有下一个元素的方法,返回值类型为布尔型
System.out.println(a.hasNext());//输出当前集合是否有下一个元素
//此方法用来返回当前下标的元素,默认下标为0
a.next();
for(Iterator it = a.iterator();it.hasNext(); ){
if(it.next().equals("xxx"){ //如果这是容器中的xxx元素
it.remove; //那么删除它
}
}
ListIterator继承自Iterator,它比起Iterator来,提供了更多的操作方法,便于List容器的使用
//这里还用ArrayList举例子
ArrayList al = new ArrayList();
ListIterator it = al.listIterator(); //注意这里是ListIterator
ListIterator下特有的方法:
ArrayList的底层数据结构是数组。也就是说数组的优缺点它都具备。优点是查询快,缺点是增删慢,空间是连续的,然后线程不同步。
构造方法与add方法扩容的分析
//Object类型的空数组
DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//无参构造方法
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//调用无参构造方法相当于在空间中开辟了一个空间,指向集合数组
public ArrayList(int){
//代码省略,int型参数构造方法,开辟一个长度为int值的Object数组
}
当第一次使用add方法时,完成Object类型数组的初始化容量10
当添加第11个元素时:
//在ArrayList源码中
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity); //最小容量增大方法
}
LinkedList的底层数据结构是链表。特点是增删快,查询慢,空间不是连续的。
addFirst(E e)
在该列表开头插入指定的元素。
addLast(E e)
将指定的元素追加到此列表的末尾。
getFirst()
返回此列表中的第一个元素。如果链表为空,则报异常。
getLast()
返回此列表中的最后一个元素。如果链表为空,则报异常。
peekFirst() 返回链表第一个元素,如果链表为空,则返回null
peelLast() 返回列表最后一个元素,如果链表为空,则返回null
removeFirst()
返回链表第一个元素,并删除之。如果列表为空,则报异常
removeLast()
返回链表最后一个元素,并删除之,如果链表为空,则报异常
pollFirst()返回链表第一个元素,并在链表内删除之,如链表为空,饭返回null
pollLast()返回链表最后一个元素,并删除之,如链表为空,则返回null
private static class Node { //Node即节点,这是一个节点类,内部类(在LinkedList类中)
E item; //数据本身
Node next; //后继节点
Node prev; //前驱节点
Node(Node prev, E element, Node next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
Vector在Collection框架之前出现,是元老级容器。它的底层数据结构也是数组,它与ArrayList相比的特点是,它线程同步,增删查改都慢,被后来出现的ArrayList替代。
elements返回的是一个枚举。它的功能和迭代器几乎相同,在可选的情况下建议选择迭代器。
(01) 函数接口不同
Enumeration只有2个函数接口。通过Enumeration,我们只能读取集合的数据,而不能对数据进行修改。
Iterator只有3个函数接口。Iterator除了能读取集合的数据之外,也能数据进行删除操作。
(02)Iterator支持fail-fast机制,而Enumeration不支持。
Enumeration 是JDK 1.0添加的接口。使用到它的函数包括Vector、Hashtable等类,这些类都是JDK 1.0中加入的,Enumeration存在的目的就是为它们提供遍历接口。Enumeration本身并没有支持同步,而在Vector、Hashtable实现Enumeration时,添加了同步。
而Iterator 是JDK1.2才添加的接口,它也是为了HashMap、ArrayList等集合提供遍历接口。Iterator是支持fail-fast机制的:当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
1、Enumeration和Iterator接口功能相似,而且Iterator的功能还比Enumeration多,那么为什么还要使用Enumeration?这是因为Java的发展经历了很长时间,一些比较古老的系统或者类库中的方法还在使用Enumeration接口,因此为了兼容,还是需要使用Enumeration。
2、Enumeration 比 Iterator 的遍历速度更快。为什么呢?
这是因为,Hashtable中Iterator是通过Enumeration去实现的,而且Iterator添加了对fail-fast机制的支持;所以,执行的操作自然要多一些。
假如我们现在想要在一大堆数据中查找X数据。LinkedList的数据结构就不说了,查找效率低的可怕。ArrayList哪,如果我们不知道X的位置序号,还是一样要全部遍历一次直到查到结果,效率一样可怕。HashSet天生就是为了提高查找效率的。
HashSet的底层数据结构是Hash表,也称散列表,散列结构,哈希结构。而Hash表是按照哈希表计算方法来存的。
在HashSet容器的一个对象想要进行增删查改时,需要依赖两个方法,一个是hashCode(),用于判断元素的地址,另一个是equals方法,用于判断元素的值是否相同。判断顺序是先判断Hash值,如果哈希值相同则再调用equals方法判断内容。这点与List容器不同,List 容器只依赖hashCode()方法。
1.哈希表存储计算方法:
假如我们有一个数据(散列码76268),而此时的HashSet有128个散列单元,那么这个数据将有可能插入到数组的第108个链表中(76268%128=108)。但这只是有可能,如果在第108号链表中发现有一个老数据与新数据equals()=true的话,这个新数据将被视为已经加入,而不再重复丢入链表。
2.HashSet的散列单元大小如何指定:
Java默认的散列单元大小全部都是2的幂,初始值为16(2的4次幂)。假如16条链表中的75%链接有数据的时候,则认为加载因子达到默认的0.75。HahSet开始重新散列,也就是将原来的散列结构全部抛弃,重新开辟一个散列单元大小为32(2的5次幂)的散列结果,并重新计算各个数据的存储位置。以此类推下去…
3.为什么HashSet查找效率提高了:
知道了HashSet的add机制后,查找的道理一样。直接根据数据的散列码和散列表的数组大小计算除余后,就得到了所在数组的位置,然后再查找链表中是否有这个数据即可。
4.hashCode方法必须与equals方法必须兼容:
如果我们自己定义了一个类,想对这个类的大量对象组织成散列表结构便于查找。有一点一定要注意:就是hashCode方法必须与equals方法向兼容。
泛型是用来规定集合中存储什么类型的对象的一种方法
声明一个类是泛型类,使它的对象可以使用泛型
public class Hello{ //T只是一个字母,它表示的对象是一个具体的泛型对象
}
class Test{
public static void maini(String args[]){
Hello h1= new Hello;
Hello h2 = new Hello;
}
}
//定义一个泛型接口
public interface Hello{
}
---
//类型一
public class Nihao implements Hello{
}
//此种类型在创建对象时,无需指定泛型的具体类型,此处默认为String型
Nihao n = new Nihao();
---
//类型二
public class Hea implements Hello{
}
Hea n1 = new Hea();
public class MyMethod{ //定义一个泛型类
public void method1(T t){ //泛型在当前类实例化是定义
}
public void method2(Q q){//此种方法的泛型在调用此方法时明确
}
public void method3(K...k){ //可变长泛型
}
}
---
//实例化试一下
public class Test{
public static void main(String [] args){
MyMethod mm = new MyMethod();
mm.method1("dabendan");//只能传入String型
mm.method2("dabendandan");//能传入各种类型
mm.method2(123);//能传入各种类型
mm.method3("213","ewfdf");//可变长传入各种类型
}
}
使用关键字extends,表示参数化的类型可能是所指定的类型或者是此类型的子类
①输入流:数据源到程序(InputStream、Reader读进来)
②输出流:程序到目的地(OutputStream、Writer写进去)
①字节流:按照自己饿读取数据(InputStream、OutputStream)
②字符流:按照字符读取数据(Reader、Writer)
**注:在java中,出和入都是按程序来的,数据流向程序叫入,数据流出程序叫出
①节点流:可以直接从数据源或目的地读写数据
②处理流(包装流):不直接连接到数据源或目的地,是其他流进行封装。目的主要是简化操作和提高性能
①节点流处于io操作的第一线,所有的操作必须通过他们进行
②处理流可以对其他流进行处理(提高效率或操作
第一:搭建管道;第二:进行操作
1.FileInputStream的构造方法
FileInputStream f = new FileInputStream("D:\\a.txt");
File fileA = new File("D:\\b.txt");
fileA.createNewFile();
FileInputStream f = new FileInputStream(name);
FileDescriptor a =new FileDescriptor();
FileInputStream f = new FileInputStream(a);
2.FileInputStream的普通方法
read() :从这个输入流一次读取一个字节的数据
read(byte b[]):读取数据源的全部数据放到b byte数组中。我们把数组b叫做缓冲池,处理站等。缓冲池的大小由数组长度决定,比如输出长度为10表示缓冲池大小为10,一次读取10个字节的数据。
read(byte b[], int off, int len):从该输入流读取到字节数据。放入一个字节数组。off:数据中的起始偏移量(下标从0开始),len:写入的字数。
skip(long n):从当前位置跳过n个字节数。如一个文件中有ILOVEYOU内容
byte b[] =new byte[10];
long aa =4L;
f.skip(aa);
f.read(b)
//则b中只存有EYOU内容
available():返回可以读取的剩余字节数的估计值。
close():关闭流
getFD(): 获得FileDescriptor文件描述符
getChannel():返回文件通道
1.FileOutputStream的构造方法
构造方法用法与FileInputStream构造方法基本一致,详参FileInputStream构造方法
File fileA = new File("D:\\b.txt");
fileA.createNewFile();
FileOutputStream f = new FileOutputStream(fileA);
FileOutputStream f = new FileOutputStream("D:\\a.txt",true)
2.FileOutputStream的普通方法
write(f.read()) //将输入流f当前读到的一个字节写入到输出流指向的文件中
write(99) //将ASCLL码表中小写字母c对应的十进制整数99写入到输出流指向的文件中
write(byte b[]):从指定字节数组写入
write(byte b[], int off, int len)F:从指定字节数组写入。off:数据中的起始偏移量,len:写入的字数。
close():关闭流
getChannel():返回文件通道:
getFD():获得FileDescriptor文件描述符
public class TestFileInputStream {
public static void main(String[] args) throws IOException {
File name1 = new File("D:\\b.txt");
name1.createNewFile();
FileInputStream f = new FileInputStream(name1);
FileOutputStream k = new FileOutputStream("D:\\新建文本.txt");
byte[] b= new byte[(int) name1.length()];
f.read(b);
k.write(b);
f.close();
k.close();
}
}
FileReader继承自InputStreamReader
构造方法的使用
普通方法的使用
构造方法的使用
普通方法的使用
缓冲流技术大大提高了数据读取效率,当读取的文件是文本时,推荐使用字符流,其它文件可使用字节流。
在使用BufferedInputStream时先将数据读入BufferedInput提供的输入缓冲池(默认大小为8192)中,中转站需要读取时先从输入缓冲池读取。当写入时,先写入到由BufferedOutputStream提供的输出缓冲池中(默认大小8192),最后再写入目的文件。
构造方法
FileInputStream fis = new FileInputStream("D:\\b.txt");
BufferedInputStream a =new BufferedInputStream(fis);
普通方法
构造方法
特有方法
构造方法
一般方法
构造方法
一般方法
InputStream数据流转Reader数据流
构造方法
一般方法
OutputStream数据流转Writer数据流
** 构造方法**
一般方法
基本概念
构造方法
常用方法
graph LR
FileInputStream-->BufferedInputStream
BufferedInputStream-->DataInputStream
DataInputStream-->数据
graph LR
数据-->DataOutputStream
DataOutputStream-->BufferedOutputStream
BufferedOutputStream-->FileOouputStream
序列化后的对象可以保存到磁盘上,也可以在网络上传输,使得不同的计算机可以共享对象。
要想使某个类的对象能够序列化,那么需要这个类实现序列化接口Serializable
对象序列化方法 writeObject(Object e)
对象反序列化方法 readObject();
除了对象还能操作一些基本数据类型和String类型
同样输出与输入的顺序必须一致,否则报异常
被transient修饰的成员变量不被序列化
方法不会被序列化
如果一个可序列化的对象包含某个不可序列化的对象的引用,那么整个序列化的操作都会失败,并抛出一个NotSerializableException
修改了实例属性后,会影响版本号,导致反序列化不成功,解决方案:为java对象指定序列化版本号serialVersionUID(在类的左边点击黄色警告符号)
如果要复制的文件夹下文件类型不同,则使用字节流
Commons IO是apache的一个开源工具包,封装了IO操作的相关类,使用Commons IO可以很方便的读写文件。极大地提高工作效率。
工具包安装方法
点此访问安装及使用的方法教程
package cn.sxt.test;
class MyRunnable2 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
System.out.println("MyRunnable---"+i);
}
}
}
public class Test {
public static void main(String[] args) {
MyRunnable2 my = new MyRunnable2();
Thread t =new Thread(my);
t.start();
for (int i = 0; i < 13; i++) {
System.out.println("main------"+i);
}
}
}
public class You implements Marry{
@Override
public void marry() {
// TODO Auto-generated method stub
System.out.println("结婚了,好高兴!");
}
}
public class MarryCompany implements Marry{
Marry m; //声明一个Marry接口的实例
public MarryCompany(Marry m) {
super();
this.m = m;
}
public void before() { //结婚前准备方法
System.out.println("婚庆公司在做婚前准备");
}
public void after() { //善后工作
System.out.println("婚庆公司在做善后工作");
}
@Override
public void marry() {
// TODO Auto-generated method stub
this.before();
m.marry(); //具体的结婚对象m调用结婚方法
this.after();
}
}
public interface Marry {
public abstract void marry();
}
public class Test {
public static void main(String[] args) {
You y = new You();
MarryCompany mc = new MarryCompany(y);
mc.marry();
}
}
//同步代码块
synchronized(obj){
//obj为同步监听器,必须是一个对象,推荐使用共享资源的对象
}
private synchronized void saleTicked(){
//卖票方法
}
实现生产者生产商品,消费者取走商品的功能
package cn.sxt.thread;
public class Goods {
private String brand;
private String name;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Goods(String brand, String name) {
super();
this.brand = brand;
this.name = name;
}
public Goods() {
super();
}
//加同步锁的生产方法
public synchronized void set(String brand,String name) {
this.brand=brand;
this.name=name;
}
//加同步锁的取出方法
public synchronized void get() {
System.out.println("消费者取走了-------"+this.brand+"---"+this.name);
}
}
package cn.sxt.thread;
public class Productor implements Runnable {
private Goods goods;
public Productor(Goods goods) {
this.goods=goods;
}
@Override
public void run() {
for (int i=0;i<10;i++) {
if (i%2!=0) {
goods.set("旺仔", "小馒头");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else {
goods.set("娃哈哈", "矿泉水");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("生产者线程生产了---------"+goods.getBrand()+"---"+goods.getName());
}
}
}
package cn.sxt.thread;
public class Customer implements Runnable{
private Goods goods;
public Customer(Goods goods) {
this.goods=goods;
}
public Customer() {
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
goods.get();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
package cn.sxt.thread;
public class Test {
public static void main(String[] args) {
//创建共享资源
Goods g =new Goods();
//创建生产者线程
Productor p = new Productor(g);
new Thread(p).start();
//创建消费者线程
Customer c= new Customer(g);
new Thread(c).start();
}
}
这个模型,生产者会重复生产,消费者会重复取走商品,我们需要进行优化,就要引入线程通信。
package cn.sxt.thread;
public class Goods {
private String brand;
private String name;
private boolean flag;// 新增flag用于表示是否有商品
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Goods(String brand, String name) {
super();
this.brand = brand;
this.name = name;
}
public Goods() {
super();
}
//加同步锁的生产方法
public synchronized void set(String brand, String name) {
if (flag) { //如果有商品,那么我等着
try {
super.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//被唤醒后的线程执行wait()后的代码,所有这里不能加else,else后的代码与wait()是同级的
this.brand = brand;
this.name = name;
System.out.println("生产者线程生产了---------"+this.brand+"---"+this.name);
flag=true;
super.notify();
}
//加同步锁的取出方法
public synchronized void get() {
if (!flag) { //如果没有商品,那我在等待池里等着
try {
super.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("消费者取走了-------" + this.brand + "---" + this.name);
flag=false;//取完商品,把flag变为false
super.notify();
}
}
package cn.sxt.thread;
public class Productor implements Runnable {
private Goods goods;
public Productor(Goods goods) {
this.goods=goods;
}
@Override
public void run() {
for (int i=0;i<10;i++) {
//轮流生产娃哈哈矿泉水与旺仔小馒头
if (i%2!=0) {
goods.set("旺仔", "小馒头");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else {
goods.set("娃哈哈", "矿泉水");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
package cn.sxt.thread;
public class Customer implements Runnable{
private Goods goods;
public Customer(Goods goods) {
this.goods=goods;
}
public Customer() {
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
goods.get();//调用取出商品方法
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
package cn.sxt.thread;
public class Test {
public static void main(String[] args) {
//创建共享资源
Goods g =new Goods();
//创建生产者线程
Productor p = new Productor(g);
//创建消费者线程
Customer c= new Customer(g);
new Thread(p).start();
new Thread(c).start();
}
}
graph TD
创建ServerSocket(int port)对象-->在Socket上监听客户端的连接请求
在Socket上监听客户端的连接请求-->阻塞、等待连接的简历
阻塞、等待连接的简历-->接收并处理请求信息
接收并处理请求信息-->将处理结果返回给客户端
将处理结果返回给客户端-->关闭流和Socket对象
graph TD
创建Socket(String host,int port)-->向服务器发送连接请求
向服务器发送连接请求-->向服务器发送连接请求
接收服务结果-->关闭流和Socket对象
InetAddress i = InetAddress.getLocalHost();
System.out.println("获取主机IP:"+i.getHostAddress());
System.out.println("获取主机名称:"+i.getHostName());
- 根据域名得到InetAddress对象
InetAddress ia = InetAddress.getByName("www.baidu.com");
System.out.println("获取对象IP:"+ia.getHostAddress());
System.out.println("获取对象主机名:"+ia.getHostName());
- 根据IP地址得到一个InetAddress对象
InetAddress ik = InetAddress.getByName("10.123.222.130");
System.out.println("获取对象主机名:"+ik.getHostName());
System.out.println("获取IP地址:"+ik.getHostAddress());
当这个IP地址没有配置DNS域名解析时,获取主机名返回IP地址
//表示本地主机的三种写法,其中9999端口号
InetSocketAddress isa =new InetSocketAddress("hostname",9999);
InetSocketAddress isa1 = new InetSocketAddress("127.0.0.1",9999);
InetSocketAddress isa2 = new InetSocketAddress("192.168.0.112",9999);
- InetSocketAddress(InetAddress,int)
InetAddress ia = InetAddress.getLocalHost();
InetSocketAddress isa3 = new InetSocketAddress(ia,8888);
//抓取网页保存到本地
public static void main(String args[]) throws IOException {
URL url = new URL("http://www.baidu.com");
InputStream is = url.openStream();
//创建缓冲流
BufferedReader br = new BufferedReader(new InputStreamReader(is,"utf-8"));
//创建输出流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("D:\\index.html")));
String line=null;
while((line=br.readLine())!=null) {
bw.write(line);
bw.newLine();
bw.flush();
}
br.close();
bw.close();
}
客户端封装账号与密码为对象。发送到服务器端。服务器端收到对象进行反序列化,验证账号与密码是否正确,然后做出相应。做出响应可以用字节流。
//实现Serializable接口
包含:
①String name
②String password
③给定User类一个序列化编号
//(1)创建socket对象,用于连接服务器
Socket client = new Socket("localhost",10000);
//(2)获取输出流
ObjectOutputStream oos = new ObjectOutputStream(client.getOutputStream())
//(3)创建User对象
User u = new User("bjsxt","password");
//(4)User对象发送到服务器
oos.writeObject(u);
//(5)获取输出流(数据流)用于接收服务器端的响应信息
DataInputStream dis = new DataInputStream(client.getInputStream());
System.out.println(dis.readUTF());
//(6)关闭流
if(dis!=null){
dis.close();
}
if(oos!=null){
oos.close();
}
if(client!=null){
client.close();
}
//(1)创建ServerSocket对象
ServerSocket server= new ServerSocket(10000);//端口号为10000
Socket socket =server.accept();
//(2)获取输入流--(对象流)
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
User user = (User) ois.readObject();
String str="";
//(3)对用户名和密码进行验证
if("bjsxt".equals(user.getUsername())&&"password".equals(user.getPassword())){
str="登陆成功";
}else{
str="对不起,账号密码不正确"
}
//(4)获取输出流(数据流)
DateOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(str);
//(5)关闭流
if(dos!=null){
dos.close();
}
if(ois!=null){
ois.close();
}
if(socket!=null){
socket.close();
}
这样做下来的服务器一次只能接受一个客户端的接入请求,要实现多个客户端同时接入,则需要引入多线程技术
public class ServerThread implements Runnable{
@Override
public void run() {
(2)
(3)
(4)
(5)
}
}
while(true) {
Socket socket =server.accept();
//创建线程类的对象,并启动线程
ServerThread st = new ServerThread(socket);
new Thread(st).start();
}