【学习笔记】java基础核心总结

时间:2021-10-24 00:33:06


记录:windows下查询端口占用
netstat -ano
taskkill -pid 3648 -F
netstat -ano | findstr 8082

  • ​​基础部分​​
  • ​​1.JDK的主要内容如下:​​
  • ​​2.环境变量修改:​​
  • ​​3.Java程序的开发步骤​​
  • ​​4.文件命名​​
  • ​​5.字节码文件(.class文件)​​
  • ​​6.· 标识符​​
  • ​​7. Java 的50个关键字和类型转化​​
  • ​​9.数组属引用型变量​​
  • ​​10.对象和对象的引用​​
  • ​​11.反射​​
  • ​​12.自定义注解​​
  • ​​13.异常处理​​
  • ​​14.关于集合​​
  • ​​断言的使用​​
  • ​​日志​​
  • ​​泛型的使用​​
  • ​​计算8+88+888+8888+…的前12项和​​
  • ​​进阶​​
  • ​​jvm内存管理​​
  • ​​Thread与虚拟机栈​​

基础部分

1.JDK的主要内容如下:

开发工具 Java运行环境 附加库 C头文件 源代码
如果一个平台只想运行Java程序, 可以只安装JRE 。
JRE
JRE 是个运⾏环境,JDK 是个开发环境。因此写 Java 程序的时候需要 JDK,⽽运⾏ Java 程序的时候就需要JRE。⽽ JDK ⾥⾯已经包含了JRE,因此只要安装了JDK,就可以编辑 Java 程序,也可以正常运⾏ Java 程序。但由于 JDK 包含了许多与运⾏⽆关的内容,占⽤的空间较⼤,因此运⾏普通的 Java
程序⽆须安装 JDK,⽽只需要安装 JRE 即可。

2.环境变量修改:

在弹出的 “编辑系统变量 ”对话框中为Path添加的新值是%JAVA_HOME%\bin,

3.Java程序的开发步骤

编写源文件 编译源文件
使用Java编译器(javac.exe)编译源文件, 得到字节码文件。
运行程序
使用Java SE平台中的Java解释器(java.exe)来解释执行字节码文件

4.文件命名

如果源文件中有多个类,那么只能有一个类是public类;如果有—个类是public类那么源文件的名字必须与这个类的名字完全相同, 扩展名是java;如果源文件没有public类那么原文件名只要和某个类相同,拓展是.java就可以了

5.字节码文件(.class文件)

如果源文件中包含多个类 , 编译源文件将生成多个扩展名为class 的文件 , 每个扩展名是class的文件中只存放一个类的字节码 , 其文件名与该类的名字相同。 这些字节码文件被存 放在与源文件相同的目录中。
一个Java应用程序必须有一个类含有public static void main (String args[])方法是应用程序的主类
使用Java解释器(java.exe)来解释执行其字节码文件。Java应用程序总是从主类的main方法开始执行。 因此, 需进入主类字节码所在目录 , C:\chapterl> java Hello
当使用Java解释器运行应用程序时 , Java虚拟机首先将程序需要的字节码文件加载到内存 , 然后解释执行字节码文件。 当运行上述Java 应用程序时, 虚拟机将 Hello.class和Student. class加载到内存。 当虚拟机将Hello.class加载到内存时, 就为主类中的main方法分配了入口地址, 以便Java解释器调用main方法开始运行程序。需要特别注意的是, 在运行程序时, 不可以带有扩展名

6.· 标识符

由字母、下画线、美元符号和数字组成, 长度不受限制。

· 标识符的第一个字符不能是数字字符。
· 标识符不能是关键字(关键字见下面的2.1.3节)。
· 标识符不能是true、false和null(尽管true、false和null 不是 Java关键字)。例如, 以下都是标识符:HappyNewYear_ava、 TigerYear_2010、 $98apple 、 hello、 Hello

7. Java 的50个关键字和类型转化

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 short static strictfp super switch synchronized this throw throws transient try void volatile while
对千byte型变量, 分配l个字节内存 , 占8位 , 因此byte型变量的取值范围是-27r–…,27-1 。对于short型变量 , 分配2个字节内存 , 占16位,对于long型变量, 分配8个字节内存, 占64位, 因此long型变量的取值范围是-263~ 263_1。
注:Java没有无符号的byte,short,int和long, 这一点和C语言有很大的不同 。 因此, unsigned int m,是错误的变量声明 。
对千char型变量 , 分配2个字节内存 , 占16位, 最高位不是符号位, 没有负数的char。 char型变量的取值范围是0- 65 535。 对于char x =‘a’;内存x中存储的是97 ,97 是字符a在Unicode表中的排序位置。 因此, 允许将上面的变量声明写成
char x = 97;
float型float x = 22.76f,tom = 1234.987f,weight = le-12F; 需要特别注意的是常量后面必须要有后缀 f或F,对于double常量, 后面可以有后缀d或D, 但允许省略该后缀。

byte取值范围: -128至127
short取值范围-32768至32767
int取值范围:-2147483648至2147483647

将这些类型按精度从低到高排列:
byte short char int long float double
当把级别低的变量的值赋给级别高的变量时, 系统自动完成数据类型的转换。例如:
float x = 100;
如果输出x的值,结果将是100.0。
例如:
int X = 50;
floaty;
y = x;
如果输出y的值,结果将是50.0。
当把级别高的变量的值赋给级别低的变量时,必须使用类型转换运算,格式如下。
int x = (int)34.89;
long y = (long)56.98F;
int z = (int)1999L;

8.%d: 输出int类型数据。
%c: 输出 char 型数据。
%f: 输出浮点型数据, 小数部分最多保留6位。
%s: 输出字符串数据。
输出数据时也可以控制数据在命令行的位置, 例如 , %md: 输出的int型数据占m列。
¾m.nf: 输出的浮点型数据占m列, 小数点保留n位。
Systern.out.printf("%d,%f",12,23.78);
与C/C++不同 , Java 不允许在声明数组中的方括号内指定数组元素的个数
注:和C语言不同的是, Java 允许使用int型变量的值指定数组的元素的个数, 例如, int size= 30;
double number[] = new double[size]
;
对二维数组 “ 数组名length"的值是它含有的一维数组的个数
int b[] [] = new int[3] [6];
b.length值是3

创建数组后, 系统会给数组的每个元素一个默认的值, 如float型是0.0。 在声明数组的同时也可以给数组的元素一个初始值, 如:
float boy[]= { 21.3f,23.89f,2.0f,23f,778.98f};
**

9.数组属引用型变量

**两个相同类型的数组如果具有相同的引用, 它们就有完全相同的元素。 例如:

【学习笔记】java基础核心总结


【学习笔记】java基础核心总结


需要注意的是, 对于char型数组a, System.out.println(a)不会输出数组a的引用而是输出 数组a的全部元素的值

10.对象和对象的引用

对象的创建
在 Java 中,万事万物都是对象。这句话相信你⼀定不陌⽣,尽管⼀切都看作是对象,但是你操纵的却是⼀个对象的 引⽤(reference) 。在这⾥有⼀个很形象的⽐喻:你可以把⻋钥匙和⻋看作是⼀组对象引⽤和对象的组合。当你想要开⻋的时候,你⾸先需要拿出⻋钥匙点击开锁的选项,停⻋时,你需要点
击加锁来锁⻋。⻋钥匙相当于就是引⽤,⻋就是对象,由⻋钥匙来驱动⻋的加锁和开锁。并且,即使没有⻋的存在,⻋钥匙也是⼀个独⽴存在的实体,也就是说,你有⼀个对象引⽤,但你不⼀定需要⼀个对象与之关联,也就是

Car carKey;
这⾥创建的只是引⽤,⽽并⾮对象,但是如果你想要使⽤这个引⽤时,会返回⼀个异常,告诉你需要⼀个对象来和这个引⽤进⾏关联。⼀种安全的做法是,在创建对象引⽤时同时把⼀个对象赋给它。
Car carKey = new Car();
在 Java 中,⼀旦创建了⼀个引⽤,就希望它能与⼀个新的对象进⾏关联,通常使⽤ new 操作符来实现这⼀⽬的。new 的意思是,给我⼀个新对象,如果你不想相亲,⾃⼰ new ⼀个对象就好了。祝你下辈⼦幸福。

11.反射

【学习笔记】java基础核心总结

【学习笔记】java基础核心总结

【学习笔记】java基础核心总结

【学习笔记】java基础核心总结


【学习笔记】java基础核心总结


【学习笔记】java基础核心总结

12.自定义注解

【学习笔记】java基础核心总结


注意string name()这个是表示有一个name参数,而不是一个方法名。

@TARGET用于表示这个注解可以用在类上或者方法上

【学习笔记】java基础核心总结


用@interface表示自己定义的注解

13.异常处理

Error体系 :
Error类体系描述了Java运行系统中的内部错误以及资源耗尽的情形。应用程序不应该抛出这种类型的对象(一般是由虚拟机抛出)。如果出现这种错误,除了尽力使程序安全退出外,在其他方面是无能为力的。所以,在进行程序设计时,应该更关注Exception体系。

Exception体系包括RuntimeException体系和其他非RuntimeException的体系(程序本身没有问题,但由于像IOException这类问题导致的异常属于其他异常) :
① RuntimeException:RuntimeException体系包括错误的类型转换、数组越界访问和试图访问空指针等等。处理RuntimeException的原则是:如果出现RuntimeException,那么一定是程序员的错误。例如,可以通过检查数组下标和数组边界来避免数组越界访问异常。
②其他非RuntimeException(IOException等等):这类异常一般是外部错误,例如试图从文件尾后读取数据等,这并不是程序本身的错误,而是在应用环境中出现的外部错误。
“如果出现RuntimeException异常,那么就一定是你的问题”是一条相当有道理的规则。应该通过检测数组下标是否越界来避免ArrayIndexOutOfBoundsException异常;应该通过在使用变量之前检测是否为空来杜绝NullPointerException异常的发生。
Java语言规范将派生于Error类或RuntimeException类的所有异常称为未检查(unchecked)异常,所有其他的异常称为已检查(checked)异常。这是两个很有用的术语,在后面还会用到。编译器将核查是否为所有的已检查异常提供了异常处理器。

与c++对比:C++注释:如果熟悉标准C++类库中的异常层次结构,就一定会感到有些困惑。C++有两个基本的异常类,一个是runtime_error;另一个是logic_error。logic_error类相当于Java中的RuntimeException,它表示程序中的逻辑错误;runtime_error类是所有由于不可预测的原因所引发的异常的基类。它相当于Java中的非RuntimeException异常。

try关闭资源的一种写法:
假设资源属于一个实现了AutoCloseable接口的类,Java SE 7为这种代码模式提供了一个很有用的快捷方式。AutoCloseable接口有一个方法:

void close() throws Exception

注释:另外,还有一个Closeable接口。这是AutoCloseable的子接口,也包含一个close方法。不过,这个方法声明为抛出一个IOException。

带资源的try语句的最简形式:

try(Resource res){
work with res
}

try块语句退出时,会自动调用res.close(),如:要读取一个文件中所有单词:

try{Scanner in = new Scanner(new FileInputStream("/xxx/xxx"))
{
while(in.hasNext())
{
System.out.println(in.next())
}
}

如果try块抛出一个异常,而且close方法也抛出一个异常,这就会带来一个难题。带资源的try语句可以很好地处理这种情况。原来的异常会重新抛出,而close方法抛出的异常会“被抑制”。这些异常将自动捕获,并由addSuppressed方法增加到原来的异常。如果对这些异常感兴趣,可以调用getSuppressed方法,它会得到从close方法抛出并被抑制的异常列表。 你肯定不想采用这种常规方式编程。只要需要关闭资源,就要尽可能使用带资源的try语句。
注释:带资源的try语句自身也可以有catch子句和一个finally子句。这些子句会在关闭资源之后执行。不过在实际中,一个try语句中加入这么多内容可能不是一个好主意。

一下异常处理的技巧:
1.异常处理不能代替简单的测试 作为一个示例,在这里编写了一段代码,试着上百万次地对一个空栈进行退栈操作。在实施退栈操作之前,首先要查看栈是否为空。

if(!s.empty()) s.pop();

接下来,强行进行退栈操作。然后,捕获EmptyStackException异常来告知我们不能这样做。

try{
s.pop();

}
catch(EmptyStackException e)
{
}

在测试的机器上,调用isEmpty的版本运行时间为646毫秒。捕获EmptyStackException的版本运行时间为21739毫秒。 可以看出,与执行简单的测试相比,捕获异常所花费的时间大大超过了前者,因此使用异常的基本规则是:只在异常情况下使用异常机制。

2.不要过分地细化异常 很多程序员习惯将每一条语句都分装在一个独立的try语句块中。

PrintStream out;
Stack s;
for(int i = 0;i < 100;i++)
{
try{
n = s.pop();
}
catch(EmptyStackException e)
{}
try
{
out.writeInt(n);
}
catch(IOException e)
{
//problem writing to file
}
}

这种编程方式将导致代码量的急剧膨胀。首先看一下这段代码所完成的任务。在这里,希望从栈中弹出100个数值,然后将它们存入一个文件中。(别考虑为什么,这只是一个“玩具”例子。)如果栈是空的,则不会变成非空状态;如果文件出现错误,则也很难给予排除。出现上述问题后,这种编程方式无能为力。因此,有必要将整个任务包装在一个try语句块中,这样,当任何一个操作出现问题时,整个任务都可以取消。
3.利用异常层次结构 不要只抛出RuntimeException异常。
应该寻找更加适当的子类或创建自己的异常类。 不要只捕获Thowable异常,否则,会使程序代码更难读、更难维护。 考虑已检查异常与未检查异常的区别。已检查异常本来就很庞大,不要为逻辑错误抛出这些异常。(例如,反射库的做法就不正确。调用者却经常需要捕获那些早已知道不可能发生的异常。) 将一种异常转换成另一种更加适合的异常时不要犹豫。例如,在解析某个文件中的一个整数时,捕获NumberFormatException异常,然后将它转换成IOException或MySubsystemException的子类。

14.关于集合

【学习笔记】java基础核心总结


【学习笔记】java基础核心总结


【学习笔记】java基础核心总结

断言的使用

在默认情况下,断言被禁用。可以在运行程序时用-enableassertions或-ea选项启用它:

java -enableassertions MyApp

需要注意的是,在启用或禁用断言时不必重新编译程序。启用或禁用断言是类加载器(class loader)的功能。当断言被禁用时,类加载器将跳过断言代码,因此,不会降低程序运行的速度。 也可以在某个类或某个包中使用断言,例如:

这条命令将开启MyClass类以及在com.mycompany.mylib包和它的子包中的所有类的断言。选项-ea将开启默认包中的所有类的断言。 也可以用选项-disableassertions或-da禁用某个特定类和包的断言:

java -ea:MyClass -ea:com.mycompany.mylib..MyApp

这条命令将开启MyClass类以及在com.mycompany.mylib包和它的子包中的所有类的断言。选项-ea将开启默认包中的所有类的断言。 也可以用选项-disableassertions或-da禁用某个特定类和包的断言

日志

基本日志
下面从一个最简单的例子开始。日志系统管理着一个名为Logger.global的默认日志记录器,可以用System.out替换它,并通过调用info方法记录日志信息:

Logger.getGlobal().info("yes today");

【学习笔记】java基础核心总结


高级日志

从前面已经看到“虚拟日志”,下面继续看一下企业级(industrial-strength)日志。在一个专业的应用程序中,不要将所有的日志都记录到一个全局日志记录器中,而是可以自定义日志记录器。调用getLogger方法可以创建或检索记录器:

private static final Logger myLogger = Logger.getLogger("com.example.demo.lambda");

与包名类似,日志记录器名也具有层次结构。事实上,与包名相比,日志记录器的层次性更强。对于包来说,一个包的名字与其父包的名字之间没有语义关系,但是日志记录器的父与子之间将共享某些属性。例如,如果对com.mycompany日志记录器设置了日志级别,它的子记录器也会继承这个级别。
通常,有以下7个日志记录器级别:
·SEVERE
·WARNING
·INFO
·CONFIG
·FINE
·FINER
·FINEST
在默认情况下,只记录前三个级别。也可以设置其他的级别。
myLogger.setLevel(Level.FINER);

泛型的使用

有时,类或方法需要对类型变量加以约束。下面是一个典型的例子。我们要计算数组中的最小元素: 但是,这里有一个问题。请看一下min方法的代码

class ArrayAlg{
public static<T>T min(T[]a)
{
if(a == null || a.length == 0) return null;
T smallest = a[0];
for(int i = 0;i < a.length;i++)
if(smallest.compreTo(a[i]) > 0) smallest = a[i];
return smallest;
}
}

内部。变量smallest类型为T,这意味着它可以是任何一个类的对象。怎么才能确信T所属的类有compareTo方法呢? 解决这个问题的方案是将T限制为实现了Comparable接口(只含一个方法compareTo的标准接口)的类。可以通过对类型变量T设置限定(bound)实现这一点: 实际上Comparable接口本身就是一个泛型类型。目前,我们忽略其复杂性以及编译器产生的警告

public static <T extends Comparable>T min(T[]a)..

现在,泛型的min方法只能被实现了Comparable接口的类(如String、Date等)的数组调用。由于Rectangle类没有实现Comparable接口,所以调用min将会产生一个编译错误。

一个类型变量或通配符可以有多个限定,例如: 限定类型用“&”分隔,而逗号用来分隔类型变量。 在Java的继承中,可以根据需要拥有多个接口超类型,但限定中至多有一个类。如果用一个类作为限定,它必须是限定列表中的第一个。

T extends Comparable & Serializable

计算8+88+888+8888+…的前12项和

public static void rnain(String args[]) {
long sum = O,a = 8,itern = a,n = 12,i = l; for(i=l;i<=n;i++) {
sum = surn+itern;
item = itern*lO+a;
}
Systern.out.println(sum);
}
}

进阶

jvm内存管理

通过对比会发现在JDK1.7版本中存在持久代内存区域,而在JDK1.8版本中,该内存区域被Meta Space取而代之了,元空间同样是堆内存的一部分,JVM为每个类加载器分配一块内存块列表,进行线性分配,块的大小取决于类加载器的类型,sun/反射/代理对应的类加载器块会小一些,之前的版本会单独卸载回收某个类,而现在则是GC过程中发现某个类加载器已经具备回收的条件,则会将整个类加载器相关的元空间全部回收,这样就可以减少内存碎片,节省GC扫描和压缩的时间。

Thread与虚拟机栈

JVM的内存分布中,其中程序计数 器是比较小的一块内存,而且该部分内存是不会出现任何溢出异常的,与线程创建、运行、销毁等关系比较大的是虚拟机栈内存了,而且栈内存划分的大小将直接决定在一个JVM进程中可以创建多少个线程,请看下面的例子:

ThreadCounter.java package com.wangwenjun.concurrent.chapter02;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadCounter extends Thread
{

final static AtomicInteger counter = new AtomicInteger(0);

public static void main(String[] args)
{

try
{
while (true)
{
new ThreadCounter().start();
}
} catch (Throwable e)
{
System.out.println("failed At=>" + counter.get());
}
}

@Override
public void run()
{
try
{
System.out.println("The " + counter.getAndIncrement() + " thread be created.");
TimeUnit.MINUTES.sleep(10);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}

在代码中,我们不断地创建线程,直到JVM再也没有能力创建新的线程为止,我们通过设置栈内存的大小来分析栈内存大小对创建线程的影响,运行上面的程序很容易出现系统死机的情况,通过测试数据的对比我们不难看出,线程的创建数量是随着虚拟机栈内存的增多而减少的,也就是一种反比关系

其实并不难理解,根据我们前面所学的知识可以得知,虚拟机栈内存是线程私有的,也就是说每一个线程都会占有指定的内存大小,我们粗略地认为一个Java进程的内存大小为:堆内存+线程数量*栈内存。

不管是32位操作系统还是64位操作系统,一个进程的最大内存是有限制的,比如32位的Windows操作系统所允许的最大进程内存为2GB,因此根据上面的公式很容易得出,线程数量与栈内存的大小是反比关系,那么线程数量与堆内存的大小关系呢?当然也是反比关系,只不过堆内存是基数,而栈内存是系数而已, 
堆内存作为影响进程内存的基数,它的增大对线程数量的影响也是反比关系,但是并没有像栈内存那样明显,线程数量与堆内存大小的关系如图2-9所示。