Java 基础
进制
1.进制转换
- 二进制转十进制
从最低位开始将每个位上的数提取出来,乘以2的(位数-1)次方,然后相加 - 十进制转二进制
将该数不断处于2,直到商为0为止,然后将每部得到的余数倒过来 ,就是二进制 - 二进制转8进制
从低位起,将二进制数每三位 为一组,转成8进制即可 421 - 二进制转16 进制
从低位起,将二进制数每四位 为一组,转成16进制即可 8421 - 八进制转二进制
将八进制的每一位数,转成对应的一个三位数的二进制数
2.源码、反码、补码
- 对于正数,它的源码、反码、补码相同
- 负数的反码是将原码中除符号位以外的所有位(数值位)取反,也就是 0 变成 1,1 变成 0。
- 负数的补码是其反码加 1
3.位运算符
- 按位与&,运算规则:同1为一,其余为0。
- 按位或|,运算规则:两位又一个位1,结果位1,否则位0
- 按位异或^ ,两位一个为0,一个为1结果为1,否则为0.
- 按位取反~, 0变成1,1变成0.
数据类型
Java语言中的数据类型分为两大类,分别是基本类型和引用类型
基本数据类型
Java基本类型共有八种,基本类型可以分为三类,字符类型char,布尔类型boolean以及数值类型byte、short、int、long、float、double
引用数据类
类(class)、接口、数
可变参数 (int...num)
在编写方法的过程中,可能会遇见一个方法有不确定参数个数的情况。一般我们会用方法重载来解决问题
//方法重载,解决参数个数不确定问题
public void method();
public void method(int a);
public void method(int a, int b);
public void method(int a, int b, int c);
但是,当参数个数过多,这样太过于繁琐,于是我们可以使用不定项参数(可变参数)的方式:可以接受多个数据使用可变参数时候 当作数组来用
public class MethodReorganization {
public static void main(String[] args) {
Reorganization m= new Reorganization();
int a=m.sum(1,2,3,4,5,6,7,8,8);
System.out.println(a);
}
}
//可以接受多个int的数据
//使用可变参数时候 当作数组来用
class Reorganization{
public int sum(int...nums){
int res = 0;
for (int i = 0;i<nums.length;i++){
res += nums[i];
}
return res;
}
public int p(int a,int...b){
//当有多个行参时候 可变参数放在最后 最多只能有一个可变参数
return 0
}
}
冒泡排序
public class maoPao {
public static void main(String[] args) {
int [] a={7,3,8,9,4,10};
int tmp=0;
for (int i = 0; i < a.length-1; i++) {
for (int j = 0; j < a.length - 1; j++) {
if(a[j]>a[j+1]){
tmp=a[j];
a[j]=a[j+1];
a[j+1]=tmp;
}
}//显示过程
for (int k = 0; k <a.length ; k++) {
System.out.print(a[k]+"\t");
}
System.out.println();
}
}
}
构造器
构造器最大的用处就是在创建对象时执行初始化,当创建一个对象时,系统会为这个对象的实例进行默认的初始化。如果想改变这种默认的初始化,就可以通过自定义构造器来实现
- 构造器没有返回值,也不能写void
- 构造器的名称和类名(iConstructor)一样
- (String Name,int Age)是构造器形参列表,规则和成员方法一样
public class Constructor {
public static void main(String[] args) {
iConstructor a=new iConstructor("jack",80);
System.out.println("这是5 "+a.age+" "+a.name);
}
}
class iConstructor{
String name;
int age;
public iConstructor (String Name,int Age){
name=Name;
age =Age;
}
}
对象创建的流程
This关键字
- this关键学河以用来访问本类的属性、方法、构造器
- this用于区分当前类的属性和局部变量
- 访问成员方法的语法:this.方法名(参数列表);
- 访问构造器语法:this(参数列表):注意只能在构造器中使用
- .this不能在类定义的外部使用,只能在类定义的方法
public class This {
public static void main(String[] args) {
new excThis();
}
}
class excThis{
double aa;
String name;
public excThis(){
this(12,"jack");
// this 必须放在前面
//访问构造器语法:this(参数列表)注意只能在构造器中使用(即只能在构造器中访问另外一个构造器)
System.out.println("我是空格");
}
public excThis(double aa,String name){
this.name=name;
this.aa=aa;
System.out.println(this.name);
}
}
java 访问控制符
- 公开级别:用
pubslic
修饰,对外公开 - 受保护级别用:
protected
修饰,对子类和同一个包中的类公开 - 默认级利: 设有修饰符号,中司一个包的类公开
- 私有级别:用
private
修饰,只有类本身可以访问,不对外公开.
访问控制符范围 | |||||
访问级别 | 访问修饰符 | 同类 | 同包 | 子类 | 不同包 |
公开 | public | 可以 | 可以 | 可以 | 可以 |
受保护的 | protected | 可以 | 可以 | 可以 | 不可以 |
默认权限 | 可以 | 可以 | 不可以 | 不可以 | |
私有 | private | 可以 | 不可以 | 不可以 | 不可以 |
public class modifier {
//四个属性,分别使用不同的访问修饰符来修饰
public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
public void m(){
//该方法可以访间四个属性
System.out.println("n1=" + n1 + " n2=" + n2 + "n3" + n3 + " n4=" + n4);
}
}
Java 进阶
java 封装★
封装(encapsulatior)就是把抽象出的数据属性( )和対数据的操作[方法]封装在一起数据被保护在内部,程序的其它部分只有通过被授权的操作(方法)才能对数据进行操作。
- 将属性进行私有化
private
【不能直接修改属性】 - 提供一个公共的
(public)set
方法,用于对属性判断井赋值
public void setXxx(参数名) {
}
- 提供一个公共的
(public)get
方法,用于获取属性的值
public 数据类型 getXxx() {
return xxx;
}
面向对象-继承(Extend)
- 子类继承了所有的属性和方法,但是私有属性不能在子类直接访问,要通过公共的方法去访问
Student.java
package tex;
public class Student {
public int math;
private String name;
protected int bb;
public String getName() {//通过 get和set 方法来使用private属性
return name;
}
public void setName(String name) {
this.name = name;
}
public void aa(){
System.out.println("我是aa");
}
private void bb(){
System.out.println("我是bb");
}
protected void cc(){
System.out.println("我是cc");
}
public void t(){//通过公共的方法使用 private
bb();
}
}
first.java
package tex;
public class first extends Student{
public void f(){
System.out.println(getName()+math+"\t");
}
}
class main{
public static void main(String[] args) {
first first = new first();
first.setName("kunkun");
first.math=12;
first.f();
first.aa();
first.cc();
first.t();
}
- 子类必须调用父类的构造器,完成父类的初始化
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无 参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用
super()
去指定使用父类的哪个构造器完成对父类的初始化工作,否则编译不会通过,++++当父类默认构造器被覆盖了。要使用super()
来指定++++ - 如果希望指定去调用父类的某个构造器,则显式的调用一下:
super(参数列表)
super()
在使用时,需要放在构造器第一行super
只能在构造器中使用,不能放在方法中使用super()
和this()
都只能放在构造器第一行,因此这两个方法不能共存在一个构造器- java所有类都是
Object
类的子类,Object
是所有类的基类
Super 关键字
super
代表父类的引用,用于访问父类的厲性、方法、构造器
- 访问父类的属性,但不能访问父类的
private
属性super.属性名
- 访问父类的方法,不能访问父类的
private
方法super.方法名(参数列表):
- 访问父类的构造器
super(参数列表)
;只能放在构造器的第一句,只能出现一句!
方法重写
子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖
- 子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样
- 子类方法的返回类型和父类返回类型一样
- 子类方法不能缩小父类方法的访问权限
方法重载
方法名相同,形参数据类型不同
public class Queue8 {
public static void main(String[] args) {
Overload a= new Overload();
a.overload("goos");
a.overload(2,3);
System.out.println(a.overload("goos")+a.overload(2,3));
}
}
class Overload {
public double overload (int a,double b){
return a+b;
}
public String overload (String a){
return a;
}
}
在一个Java类中,定义多个同名的方法,如果方法名相同,方法参数不同,包括参数的类型和个数都不同,叫做方法的重载。
方法重载和方法重写的区别
名称 | 发生范围 | 方法名 | 形参列表 | 返回类型 | 修饰符 |
---|---|---|---|---|---|
方法重载 | 本类 | 一样 | 类型个数或者顺序至少有哟个不同 | 无需求 | 无要求 |
方法重写 | 父子类 | 一样 | 相同 | 子类重写的方法,返回的类型和父类返回的类型一致,或者是其子类 | 子类方法不能缩小父类方法的访问范围 |
多态
同一种行为有多种表现形式,例如:同一件事件发生在不同的人身上有不同的结果
向上转型:子类对象转成父类对象。
向下转型:父类对象转为子类对象
编译类型 引用类型
Java引用变量有两个类型,一个是编译时类型,还有一个是运行时类型。A a1 = new B()
A
是编译类型 B()
是运行类型,当使用该对象引用进行调用的时候,有这么一条规则,对象调用编译时类型的属性和运行时类型的方法
public class duotai {
public static void main(String[] args) {
A a = new A();
System.out.println(a.name);
a.qq();
System.out.println();
A a1 = new B(); //向上转型
System.out.println(a1.name);
a1.qq();
System.out.println();
B b = new B();
System.out.println(b.name);
b.qq();
}
}
class A{
String name="dog";
public void qq(){
System.out.println("name is dog");
}
}
class B extends A{
String name="cat";
public void qq(){
System.out.println("name is cat");
}
}
java的动态绑定机制
- 当调用对象方法的时候,该方法会和该对象的
内存地址/运行类型
绑定 - 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用
多态的应用
多态数组
public class text {
public static void main(String[] args) {
Persion[] arr=new Persion[3];
arr[0]=new Persion("jack",12);
//向上转型 使用子类的say()方法
arr[1]=new Student("nake",11,100);
arr[2]=new Student("mary",12,99);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i].say());
if (arr[i] instanceof Student) {
//向下转型 调用子类的方法
((Student)arr[i]).study();
System.out.println();
}
}
}
}
class Persion{
private String name;
private int age;
public Persion(String name, int age) {
this.name = name;
this.age = age;
}
public String say(){
return name +"\t"+age;
}
}
class Student extends Persion{
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
private double score;
//重写父类的say方法
@Override
public String say() {
return super.say()+"\tscore\t"+score;
}
//子类 特有的方法
public void study(){
System.out.printf("i am study");
}
}
“==”和equals 的区别
- “==”是运算符,如果是基本数据类型,则比较存储的值;如果是引用数据类型,则比较所指向对象的地址值。
-
equals是Object的方法,比较的是所指向的对象的地址值,一般情况下,重写之后比较的是对象的值。
- “==”是运算符
①如果比较的对象是基本数据类型,则比较的是其存储的值是否相等;
②如果比较的是引用数据类型,则比较的是所指向对象的地址值是否相等(是否是同一个对象)。
- equals是Object的方法,用来比较两个对象的内容是否相等。
- equals 方法不能用于比较基本数据类型,如果没有对 equals 方法进行重写,则相当于“==”,比较的是引用类型的变量所指向的对象的地址;
类变量
-
什么时候需要用类变量
当我们需要让某个类的所有对象都共享一个变量,就可以考虑使用类变量(靜态变量):比如:定义学生类,统计所有学生共交多少钱。Student (name, fee)
-
类变量与实例变量(普通属性)区别
类变量是该类的所有对象共享的,而实例是每个对象独享的
public class text {
public static void main(String[] args) {
A make = new A("make");
make.pay(100);
A tom = new A("tom");
tom.pay(300);
System.out.println(A.fee);
A.show();// 可以使用类名.static 方法调用
}
}
class A{
public String name;
public static double fee;//静态变量 fee
public A(String name) {
this.name = name;
}
public void pay(double fee){
A.fee +=fee;
}
public static void show(){
System.out.println(A.fee);
}
}
静态变量和静态方法的使用场景
如果我们希望不创建实例,也可以调用某个方法(即当做工具来使用)这时,把方法做成静态方法时非常合适
例如:system.out.println(“9开平方的结果是="+Math.sqrt(9))
使用math的sqrt方法的时候 就可以直接调用。不用再new一个对象 然后再调用
代码块
基本语法
[修饰符]{//修饰符可选,要写的话,也只能写static
代码
}; //;号可以号上,也可以省略。
static
代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,井且只会执行一次。如果是普通代码块,每(new)创建一个对象就执行。
类什么时候被加载
- 创建对象实例时(new)
- 创建子类对象实例,父类也会被加载
- 使用类的静态成员时(静态属性,静态方法)
创建一个对象时,在一个类调用顺序是
静态属性初始化、静态代码块->普通属性初始化、普通代码块->构造器
构造器的最前面其实隐含了super()和调用普通代码块,
class A {
public A() {//构造器
//这里有隐藏的执行要求
//(1)super()
//(2)调用普通代码块和普通属性初始化
System.out.printin("ok");
}
}
我们看一下创建一个子类对象时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
- 父类的静态代码块和静态属性
- 子类的静态代码块和静态属性
- 父类的普通代码块和普通属性初始化
- 父类的构造方法
- 子类的普通代码块和普通属性初始化
- 子类的构造方法
例子:
//先进行类加载 先加载父类再加载子类
//创建对象后。先super()调用父类普通代码块和普通属性初始化 再调用本类的普通代码块和普通属性初始化
public class text{
public static void main(String[] args) {
new B();
}
}
class A{
public static int aa=aa();
public int bb=bb();
public A(){
System.out.println("这是A的构造器");
}
static{
System.out.println("这是A的静态代码块");
}
public static int aa(){
System.out.println("这是A的静态属性初始化");
return 0;
}
public int bb(){
System.out.println("这是A的普通属性初始化");
return 0;
}
{
System.out.println("这是A的普通代码块");
}
}
class B extends A{
public static int cc=cc();
public int dd=dd();
public B(){
//super();先加载父类
//普通代码块
System.out.println("这是B的构造器");
}
static{
System.out.println("这是B的静态代码块");
}
public static int cc(){
System.out.println("这是B的静态属性初始化");
return 0;
}
public int dd(){
System.out.println("这是B的普通属性初始化");
return 0;
}
{
System.out.println("这是B的普通代码块");
}
}
运行结果:
这是A的静态属性初始化
这是A的静态代码块
这是B的静态属性初始化
这是B的静态代码块
这是A的普通属性初始化
这是A的普通代码块
这是A的构造器
这是B的普通属性初始化
这是B的普通代码块
这是B的构造器
final 关键字
final 可以修饰类、属性、方法和局部变量.
在某些情况下,程序员可能有以下需求,就会使用到final:
- 当不希望类被继承时,可以用final修饰
- 当不希望父类的某个方法被子类覆盖/重号(override)时,可以用final关键字修饰。
- 当不希望类的的某个属性的值被修改,可以用final修饰.
- 当不希望某个局部变量被修改,
抽象类(abdtract)
- 抽象类不能被实例化(new)
- 抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
- 一旦类包含了abstract方法,则这个类必须声明为abstract
- abstract 只能修饰类和方法,不能修属性
- 抽象方法不能使用private、final和static来修饰,
当有多介类,完成不同的任各job,要求统计得到各自完成任务的时间
抽象模版模式(提升代码的复用性)
public class t {
public static void main(String[] args) {
new b().calculateTime();
new c().calculateTime();
}
}
abstract class a{//模版 公共的相的方法
public abstract void job();//抽象方法
public void calculateTime(){
long start= System.currentTimeMillis();
job();
long end = System.currentTimeMillis();
System.out.println("运行时间"+(end-start));
}
}
class b extends a{//
@Override
public void job() {
int num ;
for (int i = 0; i < 9990000; i++) {
num=+i;
}
}
}
class c extends a{//
@Override
public void job() {
int num ;
for (int i = 0; i < 10000; i++) {
num=+i;
}
}
}
接口
接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法号出来。
interface 接口名{
//属性
//方法(1.抽象方法 2.默认方法 3.静态方法)
}
class 类名 implements 接口{
//自己的方法
//自己的属性
//必须实现的方法
}
- 接口不能被实例化
- 接口中所有的方法是public方法,接口中抽象方法,可以不用abstract修饰
- 一个普通类实现接口,就必须将该接口的所有方法都实现,可以使用alttenter来解决
- 抽象类去实现接口时,可以不实现接口的抽象方法
- 接口中的属性,只能是final的,而且是
public static final
修饰符
内部类
分类:
-
局部内部类 -->是定义在外部类的局部位置,比如方法中,井且有类名。
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,但是可以使用final修饰
- 作用域:仅仅在定义它的方
- 局部内部类---访问-->外部类的成员【访问方式:直接访问】
- 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访间外部类的成员,使用(外部类名.this.成员),去访问
-
匿名内部类
匿名内部类适合创建那种只需要一次使用的类
new 类或接口(参数列表){ //类体 }
从上面的定义可以看出,匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类,或实现一个接口。
-
成员内部类
-
静态内部类
枚举(enum)
Java 枚举是一个特殊的类,一般表示一组常量们必入一年四季,一年12个月份,一个星期的7天
Java 枚举类使用 enum 关键字来定义,各个常量使用逗号 , 来分割。
enum Color
{
RED, GREEN, BLUE;
}
异常处理
-
执行过程中所发生的异常事件可分为两大类
- Error(错误):Java虛拟机无法解决的严重问题。如:JVM系统内部错误、资源 、耗尽等严重情况。比如:StackOverflowError[栈溢出]和OOM(out of memory),Error是严重错误,程序会崩溃。
- Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试恩读取不存在的文件,网络连接中 断等等,Exception分为两大类:运行时异常【程序运行时,发生的异常】和编译时异常【编程时,编泽器检查出的异常】
-
异常包括
- NullPointerException 指针异常
- ArithmeticException 数学运算异常
- ArraylndexOutOfBoundsException 数组下标越界异常
- ClassCastException 类型转换异常
- NumberFormatException数
-
异常处理的方式
- try-catch-finally
程序员在代码中捕获发生的异常,自行处理
- throws
将发生的异常抛出,交给调用者(方法)来处理,最顶级
当try代码块中有return时,会先把return的值放在一块临时空间中,再执行finally代码块中,最后返回return临时空间中存储的值。如果finnally中也有return语句则会更新return临时空间中的值。
public class Test {
public static void main(String[] args) {
System.out.println(test());
}
private static int test() {
int temp = 1;
try {
System.out.println(temp); 1
return ++temp; 3
} catch (Exception e) {
System.out.println(temp);
return ++temp;
} finally {
++temp;
System.out.println(temp);2
}
}
}
//
执行顺序为:
输出try里面的初始temp:1;
保存return里面temp的值:2;
执行finally的语句temp:3,输出temp:3;
返回try中的return语句,返回存在里面的temp的值:2; 输出temp:2。
常用类
包装类基本类型转换
- jdk5 前的手动装箱和拆箱方式,装箱:基本类型 -> 包装类型,反之,拆箱
- jdk5 以后(含jdk5)的自动装箱和拆箱方式
- 自动装箱底层调用的是valueOf方法,比如Integer.valueOf()
public class Integer01 {
//演示int <--> Integer 的装箱和拆箱
//jdk5前是手动装箱和拆箱
//手动装箱 int -> Integer
int n1 = 100;
Integer integer = new Integer(n1);
Integer integer1 = Integer.valueOf(n1);
//手动拆箱
//Integer -> Int
int i = integer.intValue();
//jdk5后,就可以自动装箱和自动拆箱
int n2 = 200;
//自动装箱 int -> Integer
Integer integer2 = n2; //底层使用的是 Integer.valueOf(n2)
//自动拆箱 Integer -> int
int n3 = integer2; //底层仍然使用的是 intValue()方法
}
基本类型和包装类型的区别
基本类型直接在栈中存储它的具体数值,而包装类型则存储的是堆中的引用,所以基本数据类型不需要new关键字,而包装类型需要new在堆中进行分配内存空间。
什么是拆箱
• 拆箱:将包装类类型转换为基本数据类型
• 拆箱调用Integer.intValue方法
什么是装箱
• 装箱:将基本数据类型转换为包装类类型
• 装箱调用的Integer.valueOf方法
自动装箱和自动拆箱
Java1.5 之前
Integer a = Integer.valueOf(100); //手动装箱
int b = a.intValue(); //手动拆箱
Java1.5 之后
Integer a = 100; //自动装箱
int b = a; //自动拆箱
- 基本类型和包装类型进行
==
比较,包装类型会自动拆箱,直接和基本类型比较值
int a = 9;
Integer b = 9;
System.out.println(a == b);
//上述代码的结果为 true。
- 当需要进行自动装箱时,如果数字在
-128 - 127
之间,会直接使用缓存中的对象,而不是重新创建一个对象。
Integer A = 199;
int a = A;
执行第一句代码的时候,系统为我们执行了:
Integer A = Integer.valueOf(199);
执行第二句代码的时候,系统为我们执行了:
int a = A.intValue();
Integer a = 100;
Integer b = 100;
System.out.println(a == b);
//返回true
Integer a = 199;
Integer b = 199;
System.out.println(a == b);
//返回false
自动装箱是通过 Integer.valueOf() 完成的,看它的源码
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
IntegerCache
是什么,让我们一探究竟。
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
在Integer类中有一个静态内部类 IntegerCache
,在 IntegerCache
类中有一个Integer数组,用以缓存当数值范围为-128~127时的Integer对象。
所以一开始的代码 Integer a = 100;Integer b = 100;System.out.println(a == b)
;的结果是 true,因为100 在-128~127范围之内,直接从缓存池中拿的。
而 Integer a = 199;Integer b = 199;System.out.println(a == b)
;的结果是 false,因为199 不在这个范围之内,所以 new 出来了两个 Integer 对象。既然是new出来的,那就会在堆空间中产生不同的对象,不同的对象在进行 ==比较的时候,比较的是 内存中的地址
,不同对象的内存地址肯定不一样,所以返回false
Integer a = 9999;
Integer b = 9999;
System.out.println(a + 1 == b + 1);
//返回true
上述代码的运行结果是true,这是因为这2个包装类型的Integer在运算时会自动拆箱,变成2个基本数据类型的比较,相当于在判断
a.intValue() + 1 == b.intValue() + 1
所以返回
stringBulider类
- StringBuilder和StringBuffer非常类似,均代表可変的字符序列,両目方法也一样
- tring:不可变字符序列,效率低,但是复用率高
- StringBuffer:可变字符序列、效率较高(增删)、线程安全
- StringBuilder:可変字符序列、效率最高、袋程不安全 如果是单线程可以使用
String Buffer 和String Builder使用的原则,结论:
- 如果字符串存在大量的修改操作,一般使用 String Buffer 式String Burlder
- 如果字符串存在大量的修改操作,井在単袋程的情況,使用 String Builder
- 如果字符串存在大量的修改操作,井在多浅程的情況,使用 String Buffer
- 如果我们字符串很少修改,被多多个对象引用了,使用String,比如配置信息等
java System 方法
- exit 退出程序
- 复制数组元素: System arraycopy(五个参数)——>(原数组,从原数组那个索引开始拷贝,目标数组,把原数组的数据拷贝到目标数组的索引,从原数组拷贝多少个数据到目标数组)
- System currentTimeMillens : 返回当前时间距离1970-1-1的毫秒数。
Collection接口和常用方法
-
Collection 接口实现类的特点
Public interface Collection
extends Iterable -
collection实现于类可以存放多个元素,每个元素可以是Object
-
有些 Collection的实现类,可以存放重复的元素,有些不可以
-
有些Gollection的实现类,有些是有序的(List),有些不是有序(Set)
-
Collection接口没有直接的实现子类,是通过它的子接口set 和list来实现的
增强for和Iterator
用来遍历集合
for 的底层是使用 Iterator 来实现的
public class good {
@SuppressWarnings({"all"})
public static void main(String[] args) {
List arrayList = new ArrayList();
arrayList.add(new Dog("Dog",1));
arrayList.add(new Dog("Dof",2));
arrayList.add(new Dog("Dod",3));
for (Object o :arrayList) {
System.out.println(o);
}
System.out.println("======================");
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
}
}
class Dog{
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Arrylist 和Linkedlist的比较
地层结构 | 增删效率 | 该查效率 | |
---|---|---|---|
Arrylist | 可变数组 | 较低 数组扩容 | 较高 |
Linkedlist | 双向链表 | 较高,通过链表追加 | 较低 |
Hashset (实现set接口)
-
Set 接口对象存放对象数据是无序的(添加的顺序和取出的顺序不同)每次取出数据的顺序是固定的
-
Hashset 只能放一个null
-
Hashset 的底层是去实现HashMap 方法。
- 添加一个元素的时候,先得到Hash值->转化成索引
- 找到储存的位置table ,看这个索引位置是否已经存放元素,
- 没有直接加入,如果有调用equals方法比较
- 如果相同就放弃添加,如果没有这添加(挂载)到后面
TCP字节流编程
服务端
package ioRead;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class serverSide01 {
public static void main(String[] args) throws Exception {
// 服务端在10086端口监听
ServerSocket serverSocket = new ServerSocket(10086);
//等待连接
Socket accept = serverSocket.accept();
//读取客户端发送的数据
//通过socket得到一个输入流
BufferedInputStream bufferedInputStream = new BufferedInputStream(accept.getInputStream());
byte[] bytes = streamToByteArry(bufferedInputStream);
//讲byte数据写入到指定文件
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("/Users/epoch/Downloads/IO/src/2.png"));
bufferedOutputStream.write(bytes);
//向客户端回复“收到图片”
//通过socket获取到输出流
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
bufferedWriter.write("收到图片");
bufferedWriter.flush();//把内容刷新到数据通道
accept.shutdownOutput();//设置写入结束标记
// 关闭资源
bufferedWriter.close();
bufferedOutputStream.close();
bufferedInputStream.close();
serverSocket.close();
}
public static byte[] streamToByteArry(InputStream inputStream) throws Exception {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int len;
while ((len = inputStream.read(bytes)) != -1) {
byteArrayOutputStream.write(bytes, 0, len);
}
byte[] byteArray = byteArrayOutputStream.toByteArray();
byteArrayOutputStream.close();
return byteArray;
}
}
客户端
package ioRead;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class clientSide01 {
public static void main(String[] args) throws Exception {
Socket socket = new Socket(InetAddress.getLocalHost(), 10086);
//创建读取文件输入流
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("/Users/epoch/Downloads/IO/src/1.png"));
byte[] bytes = streamToByteArry(bufferedInputStream);
//通过socket获取到输出流,讲byte数据发送给客户端
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
bufferedOutputStream.write(bytes);//将文件对应的字节文件数组的内容,写到数据通道
bufferedInputStream.close();
socket.shutdownOutput();//设置写入数据的结束标记
//接收回复消息
InputStream inputStream = socket.getInputStream();
String s = streamToString(inputStream);
System.out.println(s);
//关闭相关的流
inputStream.close();
bufferedOutputStream.close();
socket.close();
}
public static byte[] streamToByteArry(InputStream inputStream) throws Exception {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int len;
while ((len = inputStream.read(bytes)) != -1) {
byteArrayOutputStream.write(bytes, 0, len);
}
byte[] byteArray = byteArrayOutputStream.toByteArray();
byteArrayOutputStream.close();
return byteArray;
}
}
UDP
- 编写一个接收端A,和一个发送端B
- 接收端A在 9999端口等待接收数据(receive)
- 发送端A向接收端B 发送 数据“hello,明天吃火锅~"
- 接收端B接收到 发送端A发送的数据,回复“好的,明天见”,
package UDP;
//serviceSide
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPReceiverA {
public static void main(String[] args) throws IOException {
//创建一个DatagramSocket对象,准备咋i0086端口接受收据
DatagramSocket datagramSocket = new DatagramSocket(10086);
//构建一个DatagramSocket对象,准备接收数据
//一个数据包最大64k
byte[] bytes = new byte[64 * 1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
//接受数据 通过网络传输的DatagramSocket对象填充到packet对象
datagramSocket.receive(datagramPacket);
//packet 进行拆包取出数据,显示出来
int length = datagramPacket.getLength();
byte[] data = datagramPacket.getData();
String s = new String(data, 0, length);
System.out.println(s);
//发送
byte[] bytes1 = "hello,我是接送端".getBytes();
datagramSocket.send(new DatagramPacket(bytes1, bytes1.length, InetAddress.getLocalHost(),25565));
//关闭
datagramSocket.close();
}
}
package UDP;
import java.io.IOException;
import java.net.*;
public class UDPSenderB {
public static void main(String[] args) throws IOException {
//创建DatagramSocket 对象,准备接受数据
DatagramSocket datagramSocket = new DatagramSocket(25565);
//讲需要发送的数据封装到DatagramPacket 对象
byte[] bytes = "hello,我是发送端".getBytes();
//封装DatagramPacket 对象的data内容字节数组,data.length,主机端口
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(),10086);
//通过网络传输的DatagramSocket对象发出packet数据包
datagramSocket.send(datagramPacket);
datagramSocket.receive(new DatagramPacket("hello".getBytes(),"hello".getBytes().length));
int length = datagramPacket.getLength();
byte[] data = datagramPacket.getData();
String s = new String(data, 0, length);
System.out.println(s);
datagramSocket.close();
}
}