how2j学习笔记

Java 初级

数据类型转换

练习:

short a = 1;
short b = 2;
那么 a+b 是什么类型?

答案:

变量的命名规则:

变量命名只能使用字母 数字 $ _
变量第一个字符 只能使用 字母 $ _
变量第一个字符 不能使用数字

变量不能使用关键字
注:_ 是下划线,不是-减号或者—— 破折号

作用域

        练习:属性的作用域在方法中,参数的作用域也在方法中,如果属性和参数命名相同了的话? 那么到底取哪个值?答案

public class HelloWorld {
    int i = 1; //属性名是i
    public void method1(int i){ //参数也是i
        System.out.println(i); 
    }
    
    public static void main(String[] args) {
		new HelloWorld().method1(5);
		//结果打印出来是 1还是5?
	}
}

输出应该是5

在堆中的确是将1赋值给i;

但是在栈中又new了一个新值,且其地址指向堆中的i使得i的值又变回5

final

final修饰的类不能被继承

final定义的方法不能被重写

 final定义的常量不能被重写赋值 

自增 自减操作符

public class HelloWorld {
	public static void main(String[] args) {
		int i = 5;
		System.out.println(i++); //输出5
		System.out.println(i);   //输出6
		
		int j = 5;
		System.out.println(++j); //输出6
		System.out.println(j);	 //输出6
	}
}

 int i = 1;

int j = ++i + i++ + ++i + ++i + i++;

System.out.println(j); // j=2+2+4+5+5=18

public class HelloWorld {
	public static void main(String[] args) {
		//长路与  无论第一个表达式的值是true或者false,第二个的值,都会被运算
		int i = 2;
		System.out.println( i== 1 & i++ ==2  ); //无论如何i++都会被执行,所以i的值变成了3
		System.out.println(i);
		
		//短路与 只要第一个表达式的值是false的,第二个表达式的值,就不需要进行运算了
		int j = 2;
		System.out.println( j== 1 && j++ ==2  );  //因为j==1返回false,所以右边的j++就没有执行了,所以j的值,还是2
		System.out.println(j);		
		
	}
}

switch可以使用byte,short,int,char,String,enum
注: 每个表达式结束,都应该有一个break;
注: String在Java1.7之前是不支持的, Java从1.7开始支持switch用String的,编译后是把 String转化为hash值,其实还是整数
注: enum是枚举类型,在枚举章节有详细讲解 

 冒泡排序:

public void BubbleSort(int[] arr){
		for(int i=0;i<arr.length-1;i++){ 
			for(int j=0;j<arr.length-1-i;j++){
				if(arr[j]>arr[j+1]){ //相邻两个元素作比较,如果前面元素大于后面,进行交换
					int temp = arr[j+1];
					arr[j+1] = arr[j];
					arr[j] = temp;
				}
			}
		}
	}

// 优化
public class BubbleSort implements IArraySort {

    @Override
    public int[] sort(int[] sourceArray) throws Exception {
        // 对 arr 进行拷贝,不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

        for (int i = 1; i < arr.length; i++) {
            // 设定一个标记,若为true,则表示此次循环没有进行交换,也就是待排序列已经有序,排序已经完成。
            boolean flag = true;

            for (int j = 0; j < arr.length - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    int tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;

                    flag = false;
                }
            }

            if (flag) {
                break;
            }
        }
        return arr;
    }
}

 //增强型for循环遍历
        for (int each : values) {
            System.out.println(each);
        }

 数组常用的方法Java 数组 常用方法 - 简书

引用和指向

new Hero();

代表创建了一个Hero对象

但是也仅仅是创建了一个对象,没有办法访问它
为了访问这个对象,会使用引用来代表这个对象

Hero h = new Hero();
h这个变量是Hero类型,又叫做引用
=的意思指的h这个引用代表右侧创建的对象
“代表” 在面向对象里,又叫做“指向”

继承的特性

  • 子类拥有父类非 private 的属性、方法。

  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。

  • 子类可以用自己的方式实现父类的方法。

  • Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。

  • 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)

super 与 this 关键字

super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。

this关键字:指向自己的引用。

class Animal {
  void eat() {
    System.out.println("animal : eat");
  }
}
 
class Dog extends Animal {
  void eat() {
    System.out.println("dog : eat");
  }
  void eatTest() {
    this.eat();   // this 调用自己的方法
    super.eat();  // super 调用父类方法
  }
}
 
public class Test {
  public static void main(String[] args) {
    Animal a = new Animal();
    a.eat();
    Dog d = new Dog();
    d.eatTest();
  }
}

 animal : eat

dog : eat

animal : eat

重写(Override)与重载(Overload) 

    重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变

 方法的重写规则

  • 参数列表与被重写方法的参数列表必须完全相同。

  • 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。

  • 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。

  • 父类的成员方法只能被它的子类重写。

  • 声明为 final 的方法不能被重写。

  • 声明为 static 的方法不能被重写,但是能够被再次声明。

  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。

  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。

  • 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。

  • 构造方法不能被重写。

  • 如果不能继承一个类,则不能重写该类的方法。

当需要在子类中调用父类的被重写方法时,要使用 super 关键字。 

重载(Overload)

        重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。最常用的地方就是构造器的重载。

重载规则:

  • 被重载的方法必须改变参数列表(参数个数或类型不一样);
  • 被重载的方法可以改变返回类型;
  • 被重载的方法可以改变访问修饰符;
  • 被重载的方法可以声明新的或更广的检查异常;
  • 方法能够在同一个类中或者在一个子类中被重载。
  • 无法以返回值类型作为重载函数的区分标准。
public class Overloading {
    public int test(){
        System.out.println("test1");
        return 1;
    }
 
    public void test(int a){
        System.out.println("test2");
    }   
 
    //以下两个参数类型顺序不同
    public String test(int a,String s){
        System.out.println("test3");
        return "returntest3";
    }   
 
    public String test(String s,int a){
        System.out.println("test4");
        return "returntest4";
    }   
 
    public static void main(String[] args){
        Overloading o = new Overloading();
        System.out.println(o.test());
        o.test(1);
        System.out.println(o.test(1,"test3"));
        System.out.println(o.test("test4",1));
    }
}

 总结

        方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。

  • (1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
  • (2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
  • (3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。

成员变量有四种修饰符
        private 私有的
        package/friendly/default 不写
        protected 受保护的
        public 公共的

对象属性的初始化有三种方式:

1. 声明该属性的时候初始化
2. 构造方法中初始化
3. 初始化块

问题:这三种方式,谁先执行?谁后执行? 

单例模式:

单例模式又叫做 Singleton模式,指的是一个类,在一个JVM里,只有一个实例存在。

package charactor;

public class Singleton{

    //私有化构造方法使得该类无法在外部通过new 进行实例化
	private Singleton(){
		
	}

	//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个

	private static Singleton instance = new GiantDragon();
	
	//public static 方法,提供给调用者获取12行定义的对象
	public static Singleton getInstance(){
		return instance;
	}
	
}

package charactor;
 
public class Singleton{
  
    //私有化构造方法使得该类无法在外部通过new 进行实例化
    private Singleton(){       
    }
  
    //准备一个类属性,用于指向一个实例化对象,但是暂时指向null
    private static Singleton instance;
      
    //public static 方法,返回实例对象
    public static Singleton getInstance(){
        //第一次访问的时候,发现instance没有指向任何对象,这时实例化一个对象
        if(null==instance){
            instance = new Singleton();
        }
        //返回 instance指向的对象
        return instance;
    }
      
}

饿汉式是立即加载的方式,无论是否会用到这个对象,都会加载。
如果在构造方法里写了性能消耗较大,占时较久的代码,比如建立与数据库的连接,那么就会在启动的时候感觉稍微有些卡顿。懒汉式,是延迟加载的方式,只有使用的时候才会加载。
看业务需求,如果业务上允许有比较充分的启动和初始化时间,就使用饿汉式,否则就使用懒汉式 

什么是单例模式?
回答的时候,要答到三元素
1. 构造方法私有化
2. 静态属性指向实例
3. public static的 getInstance方法,返回第二步的静态属性

枚举类成员

枚举跟普通类一样可以用自己的变量、方法和构造函数,构造函数只能使用 private 访问修饰符,所以外部无法调用。

枚举既可以包含具体方法,也可以包含抽象方法。 如果枚举类具有抽象方法,则枚举类的每个实例都必须实现它。

enum Color
{
    RED, GREEN, BLUE;
 
    // 构造函数
    private Color()
    {
        System.out.println("Constructor called for : " + this.toString());
    }
 
    public void colorInfo()
    {
        System.out.println("Universal Color");
    }
}
 
public class Test
{    
    // 输出
    public static void main(String[] args)
    {
        Color c1 = Color.RED;
        System.out.println(c1);
        c1.colorInfo();
    }
}

接口 

接口与类的区别:

  • 接口不能用于实例化对象。
  • 接口没有构造方法。
  • 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
  • 接口不能包含成员变量,除了 static 和 final 变量。
  • 接口不是被类继承了,而是要被类实现。
  • 接口支持多继承。

接口特性

  • 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
  • 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
  • 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。

抽象类和接口的区别

  • 1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
  • 2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
  • 3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
  • 4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
/* 文件名 : NameOfInterface.java */
import java.lang.*;
//引入包
 
public interface NameOfInterface
{
   //任何类型 final, static 字段
   //抽象方法
}

接口有以下特性:

  • 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
  • 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
  • 接口中的方法都是公有的。

 多态

类的多态
父类引用指向子类对象

要实现类的多态,需要如下条件
1. 父类(接口)引用指向子类对象
2. 调用的方法有重写

操作符的多态

同一个操作符在不同情境下,具备不同的作用

  • 如果+号两侧都是整型,那么+代表 数字相加
  • 如果+号两侧,任意一个是字符串,那么+代表字符串连接

多态存在的三个必要条件

  • 继承
  • 重写
  • 父类引用指向子类对象:Parent p = new Child();

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。

public class Test {
    public static void main(String[] args) {
      show(new Cat());  // 以 Cat 对象调用 show 方法
      show(new Dog());  // 以 Dog 对象调用 show 方法
                
      Animal a = new Cat();  // 向上转型  
      a.eat();               // 调用的是 Cat 的 eat
      Cat c = (Cat)a;        // 向下转型  
      c.work();        // 调用的是 Cat 的 work
  }  
            
    public static void show(Animal a)  {
      a.eat();  
        // 类型判断
        if (a instanceof Cat)  {  // 猫做的事情 
            Cat c = (Cat)a;  
            c.work();  
        } else if (a instanceof Dog) { // 狗做的事情 
            Dog c = (Dog)a;  
            c.work();  
        }  
    }  
}
 
abstract class Animal {  
    abstract void eat();  
}  
  
class Cat extends Animal {  
    public void eat() {  
        System.out.println("吃鱼");  
    }  
    public void work() {  
        System.out.println("抓老鼠");  
    }  
}  
  
class Dog extends Animal {  
    public void eat() {  
        System.out.println("吃骨头");  
    }  
    public void work() {  
        System.out.println("看家");  
    }  
}

stringBuffer 字符串常用的方法 

Java 日期时间 

Java 中级 

1、异常处理

常见的异常:

RuntimeException 

2、I\O 文件流

什么是流(Stream),流就是一系列的数据

当不同的介质之间有数据交互的时候,JAVA就使用流来实现。
数据源可以是文件,还可以是数据库,网络甚至是其他的程序

比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流
输入流: InputStream    从硬盘  --》 内存    把数据从硬盘的文件,读取到JVM(内存)。
输出流:OutputStream   从内存 --》硬盘

package com.frank.filetest;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;

/**
 * @Author: 张超杰
 * @Date: 2022/4/14
 * @Description: 使用绝对路径或者相对路径创建File对象
 */
public class TestFile {
    public static void main(String[] args) {
        try {
            File file = new File("D:/TestFile.txt");
            // 通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中
            FileInputStream fileInputStream = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        try {
            File file1 = new File("D:/testFile.txt");
            // 输出流
            FileOutputStream fos = new FileOutputStream(file1);
        } catch (FileNotFoundException e){
            e.printStackTrace();
        }
    }

}
  • InputStream字节输入流
  • OutputStream字节输出流

用于以字节的形式读取和写入数据

package com.frank.filetest;

import java.io.*;

/**
 * @Author: 张超杰
 * @Date: 2022/4/14
 * @Description: 使用绝对路径或者相对路径创建File对象
 */
public class TestFile {
    public static void main(String[] args) {
        try {
            File file = new File("D:/TestFile.txt");
            // 通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中
            FileInputStream fis = new FileInputStream(file);
            //创建字节数组,其长度就是文件的长度
            byte[] all = new byte[(int) file.length()];
            fis.read(all);
            for (byte b:all){
                System.out.println(b);
            }
            //每次使用完流,都应该进行关闭
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            // 准备文件testFile1.txt其中的内容是空的
            File file1 = new File("D:/testFile1.txt");
            // 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
            byte data[] = {88,89};

            // 创建基于文件的输出流
            FileOutputStream fos = new FileOutputStream(file1);
            // 把数据写入到输出流
            fos.write(data);
            // 关闭输出流
            fos.close();
        } catch (IOException e){
            e.printStackTrace();
        }
    }

}

注: 如果文件d:/testFile1.txt不存在,写出操作会自动创建该文件。
但是如果是文件 d:/xyz/testFile1.txt,而目录xyz又不存在,会抛出异常

package com.frank.filetest;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;

/**
 * @Author: 张超杰
 * @Date: 2022/4/14
 * @Description:
 * 找到一个大于100k的文件,按照100k为单位,拆分成多个子文件,并且以编号作为文件名结束。
 * 比如文件 eclipse.exe,大小是309k。
 * 拆分之后,成为
 * eclipse.exe-0
 * eclipse.exe-1
 * eclipse.exe-2
 * eclipse.exe-3
 */
public class SplitFile {
    public static void main(String[] args) {
        int eachSize = 100 * 1024; // 100K
        File srcFile = new File("d:/redis-benchmark.exe");
        splitFile(srcFile,eachSize);
    }
    /**
     * 拆分的思路,先把源文件的所有内容读取到内存中,然后从内存中挨个分到子文件里
     * @param srcFile 要拆分的源文件
     * @param eachSize 按照这个大小,拆分
     */
    private static void splitFile(File srcFile, int eachSize){
        if(0 == srcFile.length())
            throw new RuntimeException("文件长度为0,不可拆分");
        
        byte[] fileContent = new byte[(int) srcFile.length()];
        // 先把文件读取到数组中
        try {
            FileInputStream fis = new FileInputStream(srcFile);
            fis.read(fileContent);
            fis.close();
        }catch (IOException e){
            e.printStackTrace();
        }

        // 计算需要被划分成多少份子文件
        int fileNumber;
        // 文件是否能被整除得到的子文件个数是不一样的
        // (假设文件长度是25,每份的大小是5,那么就应该是5个)
        // (假设文件长度是26,每份的大小是5,那么就应该是6个)
        if(0 == fileContent.length % eachSize){
            fileNumber = (int) (fileContent.length / eachSize);
        }else {
            fileNumber = (int) (fileContent.length / eachSize) + 1;
        }
        for (int i = 0; i < fileNumber; i++){
            String eachFileName = srcFile.getName()+"-"+i;
            File eachFile = new File(srcFile.getParent(),eachFileName);

            byte[] eachContent;
            // 从源文件的内容里,复制部分数据到子文件
            // 除开最后一个文件,其他文件大小都是100k
            // 最后一个文件的大小是剩余的
            if(i != fileNumber - 1){ // 不是最后一个
                eachContent = Arrays.copyOfRange(fileContent,eachSize * i,eachSize * (i + 1));
            }else{ // 最后一个
                eachContent = Arrays.copyOfRange(fileContent,eachSize * i,fileContent.length);
            }
            try {
                FileOutputStream fos = new FileOutputStream(eachFile);
                fos.write(eachContent);
                fos.close();
                System.out.printf("输出的子文件%s,其大小是 %d字节",eachFile.getAbsoluteFile(),eachFile.length());
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

所有的流,无论是输入流还是输出流,使用完毕之后,都应该关闭。 如果不关闭,会产生对资源占用的浪费。 当量比较大的时候,会影响到业务的正常开展。 

缓存流

以介质是硬盘为例,字节流和字符流的弊端:
在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。

为了解决以上弊端,采用缓存流。
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。

就好比吃饭,不用缓存就是每吃一口都到锅里去铲。用缓存就是先把饭盛到碗里,碗里的吃完了,再到锅里去铲

缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作 

BufferedReader 缓存字符输入流可以一次读取一行数据

PrintWriter 缓存字符输出流, 可以一次写出一行数据

有的时候,需要立即把数据写入到硬盘,而不是等缓存满了才写出去。 这时候就需要用到flush

数据流

DataInputStream 数据输入流
DataOutputStream 数据输出流

package stream;
      
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
      
public class TestStream {
      
    public static void main(String[] args) {
        write();
        read();
    }
 
    private static void read() {
        File f =new File("d:/lol.txt");
        try (
                FileInputStream fis  = new FileInputStream(f);
                DataInputStream dis =new DataInputStream(fis);
        ){
            boolean b= dis.readBoolean();
            int i = dis.readInt();
            String str = dis.readUTF();
             
            System.out.println("读取到布尔值:"+b);
            System.out.println("读取到整数:"+i);
            System.out.println("读取到字符串:"+str);
 
        } catch (IOException e) {
            e.printStackTrace();
        }
         
    }
 
    private static void write() {
        File f =new File("d:/lol.txt");
        try (
                FileOutputStream fos  = new FileOutputStream(f);
                DataOutputStream dos =new DataOutputStream(fos);
        ){
            dos.writeBoolean(true);
            dos.writeInt(300);
            dos.writeUTF("123 this is gareen");
        } catch (IOException e) {
            e.printStackTrace();
        }
         
    }
}

System.out 是常用的在控制台输出数据的
System.in 可以从控制台输入数据

package stream;
    
import java.util.Scanner;
    
public class TestStream {
    
    public static void main(String[] args) {
         
            Scanner s = new Scanner(System.in);
             
            while(true){
                String line = s.nextLine();
                System.out.println(line);
            }
         
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值