【JAVA基础知识】-过往笔记搬运存档

这篇博客详细介绍了JAVA基础知识,包括Java的版本、JDK与JRE的区别、标识符和关键字、数据类型、数组、字符串的使用、字符串的拼接原理、StringBuilder和StringBuffer的区别、以及方法重载、访问修饰符、构造方法、多态、异常处理、输入输出流等内容。还探讨了final、static、super和this的用法,以及如何通过反射机制和序列化机制来操作和存储对象。博客着重强调了Java中字符串的不可变性、静态和非静态成员的使用,以及在多线程环境下的安全性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


基础

Java本身

JAVA SE-----Java标准版平台
jdk-----Java开发工具包
jre-----Java运行的环境
jvm-----Java虚拟机(实现跨平台,移植)

javac 编译命令
Java 执行命令
javap -c + java已编译文件名 分析程序

Java编译程序:javac.exe
字节码是一种虚拟的机器代码,不针对特定的机器,可以支持在java的任意平台上运行

java解释器:java.exe
负责将字节码解释成本地机器指令代码并运行。每个平台都有相应的java解释程序。


jdk和open-jdk

openjdk是jdk开放的原始码版本,以GPL协议的形式放出,而jdk采用JRL放出。虽然两个协议都是开源的,但是在使用上的不同在于GPL V2允许在商业上使用,而JRL只允许个人研究使用。

open-jdk不包含Deployment(部署)功能,并且代码也不完整、只包含最精简的JDK


标识符

1.标识符字母 数字 连字符 货币符号可以组成
2.第一个字符必须是字母 连字符 货币符号
3.区分大小写
4.标识符不能与关键字相同
5.标识符不能是true false null


关键字

关键字

abstract assert boolean break byte
case catch char class const
continue default do double else
enum extends final finally float
for goto if implements import
instanceof int interface long native
new package private protected public
return strictfp short static super
switch synchronized this throw throws
transient try void volatile while

java语言中,true、false和null不是关键字,但是也不能用作其他用用途。


Java数据类型

变量就是申请内存来存储值。
Java没有无符号数据类型

内置数据类型

byte 8位 有符号 以二进制补码表示的整数 -128~127 默认值0 可节省空间
short 16位 有符号 以二进制补码表示的整数 -32768~32767 默认值0 可节省空间
int 32位 有符号 以二进制补码表示的整数 -231~231-1(2 147 483 648-1) 默认值0
long 64位 有符号 以二进制补码表示的整数 -263~263-1 默认值0L longa=1000L/l
float 32位 单精度 符合IEEE 754标准的浮点数 浮点数不能用来表示精确的值 默认值0.0f
double 64位 双精度 符合IEEE 754标准的浮点数 浮点数不能用来表示精确的值 默认值0.0d
char 16位 Unicode字符 \u0000(0)~\uffff(65 535)
boolean 只有两个取值

java里double型可以除0,不会报错,有无穷大和无穷小


数组

数组初始化

  • java的数组变量是引用类型的变量,java的数组是静态的,即当数组被初始化之后该数组的长度是不可变的。
  • java中的数组必须经过初始化才能够使用,初始化是指分配内存空间、指定初始值

简而言之,java数组需要初始化,初始化后长度不可变

数组的初始化有两种方式:

  1. 静态初始化:初始化时由程序员显式指定每个数组元素的初始值,由系统决定长度
  2. 动态初始化:初始化时程序员只指定数组长度,由系统为数组元素分配初始值

java数组有length属性

java数组是静态的,一旦初始化完成,数组元素的内存空间分配即结束,程序只能改变数组元素的值,无法改变长度。
数组变量不是数组本身,它只是指向堆内存中的数组对象,因此可以改变一个数组变量所引用的数组,这样可以造成数组长度可变的假象。
数组变量是引用变量,数组对象和数组变量不一样。需要数组初始化的是数组对象。


java数组必须在初始化时给定长度,长度不可变

int[] nums = new int[5];
int[] nums = new int[]{
   1,2,3};
int[] nums = {
   1,2,3};

在java语言中定义数组变量时,不能定义其长度,只能在使用new关键字创建数组时定义长度。
Java中将数组作为引用类型,所以用new关键字创建对象,给数组分配内存


数组的复制

有两个数组,list1和list2,让list1=list2,结果并不会复制,而是让两个数组名都指向了同一个内容,即list2的内容。
如果想复制数组,可以通过逐个循环赋值
或者使用java.lang.System里的arraycopy

arraycopy(sourceArray , srcPos , targetArray , tarPos , length);

srcPostarPos 表示的是两个数组开始的位置
length是表明从源数组要复制多少个元素给目标数组

System.arraycopy(sourceArray , 0 , targetArray , 0 ,sourceArray.length);

不过这个方法并不会给目标数组分配空间,所以目标数组一定要已经有空间


可变长参数列表

public static void printMax(int a, double… numbers){
   

……}.main…….

printMax(1,new double[]{
   1,2,3});
  1. 可变长度参数只能有一个
  2. 必须是最后一个参数

字符串

字符串和String类

String s = "abc"; 将常量池里的地址给了s,“abc”是一个常量,s是变量

  • String类代表字符串。Java里的所有字符串如"abc"都作为此类的实例化实现
  • 字符串是常量,它们的值在创建后不能更改,比如创建一个"abc"后是无法改变的

字符串的拼接

s+="def";打印s得abcdef
这里+=其实相当于concat方法 ,耗内存
s=s.concat("def");该方法new了一个新的字符串返回,不在字符串池里

String s2="abc"; 
String s3 = new String("abc"); 
String s4 = new String("abc");

其中s2==s(s是上文的s)
s2 s3 s4地址不一样 new一个字符串和常量字符串地址不一样,
比较的时候比较的是地址


为什么String使用final修饰

Java中为了效率考虑,以“”包括的字符串,只要内容相同 循序、大小写相同,无论在程序代码中出现几次JVM都只会建立一个实例放在字符串池(String pool)中维护

  • 为了实现字符串池
  • 为了多线程安全
  • 字符串是不可变的,所以在它创建的时候HashCode就被缓存了,不需要重新计算,使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象

StringBuilder和StringBuffer

  • 这两个类的对象能够被多次修改并且不产生新的未使用对象
  • StringBuilder类在 java 5 中被提出,该类的方法不是线程安全的(不能同步访问)

StringBuilder
StringBuilder是一个可变对象,可以预分配缓冲区
往StringBuilder中新增字符时,不会创建新的临时对象。
StringBuilder还可以进行链式操作

StringBuilder sb = new StringBuilder(100);
sb.append(“aaa”).append(“bbbb”).append(“hhh”);
String s = sb.toString();

可以链式操作是因为源码里的append()方法会返回this,就可以不断调用自身的其他方法。

StringBuffer
构造一个字符串缓冲区,其中没有字符,初始容量为16个字符。

为什么StringBuffer是安全的

看源码就能看出来了,StringBuffer的很多操作方法都是加了Synchronized关键字。

注意

对于普通的字符串+操作,并不需要我们将其改写为StringBuilder,因为Java编译器在编译时就自动把多个连续的+操作编码为StringConcatFactory的操作。
在运行期,StringConcatFactory会自动把字符串连接操作优化为数组复制或者StringBuilder操作。

StringBuffer是Java早期的一个StringBuilder的线程安全版本,它通过同步来保证多个线程操作StringBuffer也是安全的,但是同步会带来执行速度的下降。

StringBuilder和StringBuffer接口完全相同,现在完全没有必要使用StringBuffer。


方法的重载 (静态多态)

属于静态多态

方法签名:方法名和参数类型,不包括返回值类型和修饰符。
系统调用方法是根据方法签名调用。所以不能基于不同的返回类型或修饰符来重载方法。

一个类中,方法的名字相同,但参数的个数、参数的类型或返回值类型不同

  • 方法名一定要相同
  • 方法的参数表必须不同,包括参数的类型或个数,以此区分不同的方法体。
  • 如果参数个数不同,就不管它的参数类型了
  • 如果参数个数相同,那么参数的类型必须不同
  • 方法的返回类型、修饰符可以相同,也可不同
//overloading here, user should provide nickname

public static void SendM(String nickname) {
   

String[] contents = {
   

"How are you?Alice.",

};

SendM(nickname,"DefaultLand",contents[(int)(Math.random() \* 2)],3,1);//0-2

}

public static void SendM(String name, String servername, String message, int
count, int interval) {
   

**……**

}

修饰符/访问修饰符

访问修饰符 修饰符class 类名称 extends 父类名称 implement 接口名称
(访问修饰符与修饰符的位置可以互换)

访问修饰符
public 可以被所有类访问(使用)
package 可以被同一个包中的类访问(使用) 默认的访问权限,可以省略此关键字

修饰符
final 使用此修饰符的类不能够被继承
abstract 声明抽象类


变量

  • java中没有全局变量,只有方法变量实例变量(类中的非静态变量)、类变量(类中的静态变量)
  • 方法中的变量不能有访问修饰符

声明实例变量时,如果没有赋初值,将被初始化为null(引用类型)或者0、false(原始类型)。

实例变量初始化器是一个用{}包含的语句块,在类的构造器被调用时运行,运行于父类构造器之后,构造器之前。

类变量(静态变量)也可以通过类变量初始化器来进行初始化,类变量初始化器是一个用static{}包含的语句块,只能被初始化一次。

访问修饰符
public 可以被任何类访问
protected 可以被同一包中所有类访问,可以被所有子类访问
private 只能够被当前类的方法访问

缺省/无访问修饰符 可以被同一包中的所有类访问,如果子类没有在同一个包中,也不能访问

修饰符
static 静态变量 可以被类的所有实例共享,不需要创建类的实例就可以访问静态变量
final 常值 值只能够分配一次,不能更改 建议和static一起使用
transient 告诉编译器,在类对象序列初始化时,此变量不需要持久保存
volatile指出可能有多个线程修改此变量,要求编译器优化以保证对此变量的修改能够被正确的处理


方法

访问修饰符 修饰符 返回类型 方法名(参数列表)throws 违例列表

类的构造方法不能够有修饰符、返回类型和throws子句
类的构造方法被调用时,首先调用父类构造方法,然后运行实例变量和静态变量的初始化器,然后才运行构造器本身。

如果构造器方法没有显示的调用一个父类的构造方法,那么编译器会自动为它加上一个默认的super(),而如果父类又没有默认的无参构造方法,编译器就会报错。super必须是构造方法的第一个子句。

访问修饰符
public 可以从所有类访问
protected 可以被同一包中的所有类访问,可以被所有子类访问
private 只能够被当前类的方法访问

缺省/无访问修饰符 可以被同一包中的所有类访问

修饰符
static 静态方法,并不需要创建类的实例就可以访问静态方法
final 防止任何子类重载该方法
abstract 抽象方法
native
synchronized 多线程的支撑


子类调用父类信息

  • 使用super关键字
  • 可以调用父类公有属性和方法
  • 可以调用父类protected属性和方法

public 好像没啥不能访问的
(default) 子类不能访问,不同包不能访问,同包同类ok
private 只有同一类中可访问
无访问修饰符 包访问级别


protected

具有保护访问权限的成员可以在其所属类、其所属类的子类,及其所属包中访问。不同包内不能访问,可以被所有子类访问

当子类和父类不在同一个包中时,

  • 父类成员被子类成员继承后,公用成员访问权限保持不变
  • 私有成员和具有包访问权限的成员不能在子类中访问,也不能通过子类对象或子类名访问。
  • 保护成员在子类可以直接访问,也能通过子类对象或子类名访问,但不能通过父类对象访问(如果是静态成员,可以通过父类名访问)
  • 如果子类是共用类,保护成员在父类所属包中还可以通过子类对象或子类名访问。

final

成员变量:常量不可更改
final修饰的实例变量必须显式指定初始值,并且只能在以下3个位置指定初始值

  • 定义final实例变量时指定初始值
  • 在非静态初始化块中为final实例变量指定初始值
  • 在构造器中为final实例变量指定初始值

经过编译器的处理,这三种方法都会被抽取到构造器中赋初始值。

final修饰的静态/类变量也必须显式指定初始值,且final类变量只能在2个地方指定初始值

  1. 定义final静态变量时指定初始值
  2. 在静态初始化块中为final类变量指定初始值

经过编译器的处理,这两种方法都会被抽取到静态初始化块中赋初始值

局部变量本来就需要显式地赋初始值,final修饰局部变量不能重新赋新值
修饰对象变量时,修饰的是对象的引用,不能引用另一个变量

方法:不可被重写
不过可以重载父类的方法

类:不可被继承
作用:防止扩展和重写

原理

final变量的赋值使用了putfiled指令,JVM会在该指令后加入写屏障,保证其他线程读到它的值时不会出现未初始化的情况。

我们知道写屏障和读屏障一个重要的特性就是禁止指令间的重排序,特别是对于final域来说,编译器和处理器都需要遵从如下的两条规则:

  • 任意构造函数中对一个final域的写入,与随后把这个构造对象的引用赋值给另一个引用变量,这两个操作不能重排序。言外之意:在对象引用为任意线程可见之前,对象的final域已经被正确的初始化过了。
  • 初次读一个包含final域对象的引用,与之后初次读这个final域,这两个操作之间不能重排序。言外之意:只有得到了包含final域对象的引用,才能后读到final域的值。

final关键字通过上述的两条规则,保证了在写final域和读final域时的正确性。

为什么final修饰的变量可以在构造方法中指定值


static

方法体(局部变量)和初始化块内声明的变量不能用关键字static修饰。

表示的是生命周期,静态属性/方法是先于类的实例存在的

static修饰的成员被类的所有对象所有,静态成员可以使用类名直接访问
例:public class Helloworld Helloworld.hobby

1.静态属性/变量

静态属性ab、成员变量bc

public class Apple{
   
public static int ab = 0;
private int bc;
}

每实例化一个对象都会有一个新的bc,但是ab所有对象共有一个

2.关于静态方法

static方法里不能用this,super
静态方法里没有相应的this引用

  1. 静态方法可以调用同类中的静态成员,但不能 直接 调用非静态成员
  2. 静态方法可以通过对象来访问非静态成员

普通方法可以直接调用静态成员
static只能修饰类里的成员,不能修饰外部类,不能修饰局部变量、局部内部类。


super 和 this

this
this表示当前类的对象,指向对象本身的指针

  1. 普通的直接引用
  2. 形参与成员重名,用来区分
  3. 引用自身其他构造方法

super

  1. 引用父类成员
  2. 子类中的成员变量或方法与父类中成员变量或方法同名
  3. 引用父类构造方法

super()必须写在子类构造方法的第一行
this()也需要放在构造方法的第一行
this 和 super 不能同时出现在一个构造函数里
!!!均不能在static环境中使用

this表示这个类的当前实例,super表示父类的当前实例

static是属于类的,this是类的一个对象。

注:
Construction call must be the first statement in a construction
构造调用必须是构造中的第一个语句 即super必须是第一句
子类构造中默认调用父类构造 super(); 先实例化父类对象再实例化子类


对象和类

import语句

java.lang这个包不需要导入,该包包含的是java语言的基础类,java程序会自动导入。
比如像java.lang.System

杂谈
像这种直接 new Dog();这种,经常出现。匿名对象,不多说了。
Role role1 = new Role();
Role role1:

1.declaration of role1 in main method's stack(栈)//在栈里声明对象
2.存了分配空间的地址
=new Role(): 1.allocated(分配) a space in the heap() //在堆里分配空间
2.give role1 the address of the space. //将空间的地址给role1
(The address in Java virtual machine.not real address.)
3.用构造方法来初始化role1这个对象,为它赋值
(Role()作为构造方法是用来初始化属性的,用new再初始化对象,合在一起等同于3

实例化对象

In object-oriented programming,
在面向对象的编程中,
the process of creating object with class is often referred to instantiation
通常把 用类创建对象 的 过程 称为实例化
类是抽象的,我们没有办法操作它或使用它的方法和属性
只有将这个类实例化称为一个对象,然后调用它的方法和属性
实例化对象就是由抽象到具体的过程,这个过程叫实例化


public class 与 class

类的访问权限

在编写类的时候可以使用两种方式定义
public class
class

public class 定义类:
如果一个类声明的时候使用了public
class进行了声明,则类名称必须与程序文件名称完全一致
。并且被public修饰的类可以被其他包访问

class定义类
如果一个类声明的时候使用了class进行了声明,则作为启动类的名称可以与文件名称不一致,但是执行的时候肯定执行的是生成后的名称。
没有public修饰的类,该类有包访问权限,只能在该包中使用该类,不能被其他包访问
但是在执行的时候,执行的是生成后的.class文件,生成的的.class文件名称和class名称一样。

  • 每个编译单元(文件)都只能有一个public类,即每个编译单元都有单一的公共接口,用public类实现。此时,main必须包含在public类中。
  • public类的名称必须完全与含有该编译单元的文件名称一致,包括大小写。如果不匹配,编译时错误。
  • 如果编译单元(文件)中不含有一个public类,此时编译单元文件名称可以与启动类名称不一致。即可以随意对文件命名。此时,main()不是必须要放在Public类中才能运行程序。

总的来说,一个java源文件中最多只能有一个public类,当有一个public类时,源文件名必须与之一致,否则无法编译。如果源文件中没有一个public类,则文件名可以随意取。

包含多个类的源程序文件经编译后,将会生成多个类(字节码)文件。类文件名称和源文件中类的名称一一对应并相同(每个都有自己独立的文件)java系统调用指定的那个类中的main方法。


设计栈类

栈堆是以先入先出的方式保存数据的数据结构
栈有许多应用,例如编译器使用栈来处理(process)方法调用(invocations),调用(invoke)方法时,将其参数(parameters)和局部变量(local
variables)推入栈中。当一个方法调用(call)另一个方法时,新方法的参数和局部变量被推入栈。当一个方法完成它的工作并返回给它的调用者时,它的相关空间会从栈中释放(release)。

public class TestStackOfIntegers {
   
	public static void main(String[] args) {
   
		StackOfIntegers stack = new StackOfIntegers();
		for(int i = 0;i< 10; i++)
			stack.push(i);
		while(!stack.empty())
			System.out.print(stack.pop()+" ");
	}

}

public class StackOfIntegers {
   
	private int[] elements;
	private int size;
	private static final int DEFAULT_CAPACITY = 16;
	public StackOfIntegers() {
   
		this(DEFAULT_CAPACITY);
	}
	public StackOfIntegers(int capacity) {
   
		elements = new int[capacity];
	}

	//将新的整数放入栈顶
	public void push(int value) {
   
		if(size >= elements.length) {
   
			int[] temp = new int[elements.length*2];
			System.arraycopy(elements, 0, temp, 0, elements.length);
			elements = temp;
		}
		elements[size++]=value;
	}

	//返回并且去除栈顶的元素
	public int pop() {
   
		return elements[--size];
	}

	//返回栈顶的元素
	public int peek() {
   
		return elements[size - 1];
	}

	//测试栈是否是空的
	public boolean empty() {
   
		return size == 0;
	}

	//返回栈内有多少元素
	public int getSize() {
   
		return size;
	}

}

包装类 wrapper class

JAVA的八种基本数据类型不面向对象,为每个基本数据类型设计了一个对应的类进行代表
int->Integer char->Character 其余大写第一个字母即可

–>boxing int n1=10; Integer N1=new Integer(n1);/Integer N2=n1;
–>unboxing int n2=N2.intValu

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值