题解
N - Travelling
S - Key Task
O -胜利大逃亡(续)
E - Pots
L - DNA sequence
K - Escape
N - Travelling
dfs时间超限
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(fast)
#include<iostream>
#include<cstring>
using namespace std;
int n, m;
int mp[15][15];
int vis[15];
int ans;
void dfs(int cnt,int s,int w) {
if (cnt == n) {
ans = min(ans, w);
return;
}
for (int i = 1; i <= n; i++) {
if (mp[s][i]!=0x3f3f3f3f && vis[i]!=2) {
if (vis[i] == 1) {
vis[i]++;
dfs(cnt, i, w + mp[s][i]);
vis[i]--;
}
else {
vis[i]++;
dfs(cnt+1, i, w + mp[s][i]);
vis[i]--;
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
while (cin >> n >> m) {
for (int i = 0; i < 15; i++) {
for (int j = 0; j < 15; j++) {
mp[i][j] = 0x3f3f3f3f;
}
}
ans = 0x3f3f3f3f;
int u, v, w;
for (int i = 0; i < m; i++) {
cin >> u >> v >> w;
mp[u][v] = min(mp[u][v], w);
mp[v][u] = min(mp[v][u], w);
}
for (int i = 1; i <= n; i++) {
memset(vis, 0, sizeof(vis));
vis[i] = 1;
dfs(1,i,0);
}
if (ans == 0x3f3f3f3f)
cout << "-1" << '\n';
else
cout << ans << '\n';
}
return 0;
}
思路:状态有0,1,2所以采用三进制的状态压缩,举例:1->1,3->10,9->100....
#include<cstring>
#include<algorithm>
#include <iostream>
#include<cmath>
#define maxn 0x3f3f3f3f
using namespace std;
const int Maxn = 6e4;
int bit[12];
int dp[Maxn][11]; //dp[i][j]表示在状态i下,以j为终点的最短距离
int mp[11][11];
int num[Maxn][11]; //num[i][j]表示i状态下j出现次数
void init(){ //一个数字(状态)在各个位上的表示,三进制
int s;
for (int i = 0; i < bit[11]; i++){ //枚举所有状态
s = i;
for (int j = 1; j <= 10; j++){
num[i][j] = s % 3;
s /= 3;
}
}
}
int main()
{
int n, m;
bit[1] = 1;
for (int i = 2; i < 12; i++) {
bit[i] = bit[i - 1] * 3;
}
//cout << bit[11] << '\n';
init();
while (cin >> n >> m){
memset(mp, maxn, sizeof(mp));
int x, y, d;
for (int i = 0; i < m; i++) {
cin >> x >> y >> d;
mp[x][y] = min(mp[x][y], d); //重边考虑
mp[y][x] = min(mp[y][x], d);
}
memset(dp, maxn, sizeof(dp));
for (int i = 1; i <= n; i++)
dp[bit[i]][i] = 0; //初始位置到初始位置为0
int ans = maxn;
for (int s = 0; s < bit[n + 1]; s++){ //枚举所有状态
bool flag = 1;
for (int i = 1; i <= n; i++){ //枚举n个城市
if (!num[s][i])
flag = 0; //如果存在0位,说明未去完n个城市
if (dp[s][i] == maxn) //限制条件,从第一次初始位置到初始位置为0状态开始dp
continue;
for (int j = 1; j <= n; j++) { //dp[s][i]状态到dp[s+bit[j]][j]状态
if (i == j || mp[i][j] == maxn || num[s][j] >= 2) //到本身,无路,访问次数>=2
continue;
int s1 = s + bit[j];
dp[s1][j] = min(dp[s1][j], dp[s][i] + mp[i][j]);
}
}
if (flag){
for (int i = 1; i <= n; i++)
ans = min(ans, dp[s][i]);
}
}
if (ans == maxn)
cout << "-1\n";
else
cout << ans << '\n';
}
return 0;
}
S - Key Task
思路:bfs,遇见小写字母(钥匙),如果没有,加上(利用状态压缩的方式记录钥匙),
未标记则标记(vis也要添加钥匙这一维)入队,遇见大写字母,如果有钥匙(并且)未标记则标记入队
#include <iostream>
#include<cstring>
#include<queue>
using namespace std;
struct node
{
int x, y, s;
int k;
node() {};
node(int xx, int yy, int ss, int kk) {
x = xx, y = yy, s = ss, k = kk;
}
} p[110], t, t1;
int n, m;
int key[4] = { 'b','y','r','g' };
int door[4] = { 'B','Y','R','G' };
int dir[4][2] = { {0,-1},{0,1},{-1,0},{1,0} };
bool vis[110][110][(1 << 4) + 10]; //第三维记录钥匙
char mp[110][110];
void bfs(int sx, int sy){
queue<node>q;
t.x = sx;
t.y = sy;
t.s = t.k = 0;
q.push(t);
vis[sx][sy][0] = true;
while (!q.empty())
{
t = q.front();
q.pop();
if (mp[t.x][t.y] == 'X'){
printf("Escape possible in %d steps.\n", t.s);
return;
}
for (int i = 0; i < 4; i++){
t1.x = t.x + dir[i][0];
t1.y = t.y + dir[i][1];
t1.s = t.s + 1;
t1.k = t.k;
if (mp[t1.x][t1.y] == '#' || t1.x < 0 || t1.x >= n || t1.y < 0 || t1.y >= m)
continue;
else if (islower(mp[t1.x][t1.y])) { //islower检查是否小写字母
for (int k = 0; k < 4; k++){
if (mp[t1.x][t1.y] == key[k]){
if ((t1.k & (1 << k)) == 0)//如果没有这把钥匙,就加上。
t1.k += (1 << k);
}
if (!vis[t1.x][t1.y][t1.k]){
q.push(t1);
vis[t1.x][t1.y][t1.k] = true;
}
}
}
else if (isupper(mp[t1.x][t1.y]) && mp[t1.x][t1.y] != 'X'){ //isupper检查是否大写字母
for (int k = 0; k < 4; k++) {
if (mp[t1.x][t1.y] == door[k]){
if (t1.k & (1 << k)){
if (!vis[t1.x][t1.y][t1.k]){
vis[t1.x][t1.y][t1.k] = true;
q.push(t1);
}
break;
}
}
}
}
else{
if (!vis[t1.x][t1.y][t1.k]){
vis[t1.x][t1.y][t1.k] = true;
q.push(t1);
}
}
}
}
cout<<"The poor student is trapped!\n";
}
int main()
{
int sx=0, sy=0;
while (cin>>n>>m&&n+m){
for (int i = 0; i < n; i++)
cin >> mp[i];
memset(vis, false, sizeof(vis)); //重置数据,方便下一次使用
for (int i = 0; i < n; i++){
for (int j = 0; j < m; j++) {
if (mp[i][j] == '*'){
sx = i,sy = j;
}
}
}
bfs(sx, sy);
}
return 0;
}
O -胜利大逃亡(续)
思路:先做的Key Task这道题,这道题与Key Task几乎一摸一样,只需将钥匙,门的数据改一下,
再加个限定条件要在魔王回来之前的时间到达即可
#include <iostream>
#include<cstring>
#include<queue>
using namespace std;
struct node
{
int x, y, s;
int k;
} p[110], t, t1;
int n, m,T;
int key[10] = { 'a','b','c','d','e','f','g','h','i','j'};
int door[10] = {'A','B','C','D','E','F','G','H','I','J'};
int dir[4][2] = { {0,-1},{0,1},{-1,0},{1,0} };
bool vis[20][20][(1 << 10) + 10]; //第三维记录钥匙
char mp[20][20];
void bfs(int sx, int sy) {
queue<node>q;
t.x = sx;
t.y = sy;
t.s = t.k = 0;
q.push(t);
vis[sx][sy][0] = true;
while (!q.empty())
{
t = q.front();
q.pop();
if (mp[t.x][t.y] == '^'&&t.s<T) {
cout << t.s << '\n';
return;
}
for (int i = 0; i < 4; i++) {
t1.x = t.x + dir[i][0];
t1.y = t.y + dir[i][1];
t1.s = t.s + 1;
t1.k = t.k;
if (mp[t1.x][t1.y] == '*' || t1.x < 0 || t1.x >= n || t1.y < 0 || t1.y >= m)
continue;
else if (islower(mp[t1.x][t1.y])) { //islower检查是否小写字母
for (int k = 0; k < 10; k++) {
if (mp[t1.x][t1.y] == key[k]) {
if ((t1.k & (1 << k)) == 0)//如果没有这把钥匙,就加上。
t1.k += (1 << k);
}
if (!vis[t1.x][t1.y][t1.k]) {
q.push(t1);
vis[t1.x][t1.y][t1.k] = true;
}
}
}
else if (isupper(mp[t1.x][t1.y]) && mp[t1.x][t1.y] != '^') { //isupper检查是否大写字母
for (int k = 0; k < 10; k++) {
if (mp[t1.x][t1.y] == door[k]) {
if (t1.k & (1 << k)) {
if (!vis[t1.x][t1.y][t1.k]) {
vis[t1.x][t1.y][t1.k] = true;
q.push(t1);
}
break;
}
}
}
}
else {
if (!vis[t1.x][t1.y][t1.k]&&t1.s<T) {
vis[t1.x][t1.y][t1.k] = true;
q.push(t1);
}
}
}
}
cout << "-1" << '\n';
}
int main()
{
int sx = 0, sy = 0;
while (cin >> n >> m>>T) {
for (int i = 0; i < n; i++)
cin >> mp[i];
memset(vis, false, sizeof(vis)); //重置数据,方便下一次使用
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (mp[i][j] == '@') {
sx = i, sy = j;
}
}
}
bfs(sx, sy);
}
return 0;
}
E - Pots
思路:bfs六种操作,同时用数组记录第几步操作,符合条件输出
#include<cstring>
#include<queue>
#include <iostream>
using namespace std;
const int maxn=1e5 + 10;
int a, b, c;
struct node{
int x;
int y;
int s[maxn];
int step;
}t,t1;
int vis[1010][1010];
int bfs(){
int i;
queue<node>Q;
t.x = 0;
t.y = 0;
t.step = 0;
vis[t.x][t.y] = 1;
Q.push(t);
while (!Q.empty()) {
t = Q.front();
Q.pop();
if ((t.x == c) || (t.y == c)){
cout << t.step << '\n';
for (i = 1; i <= t.step; i++){//输出操作步骤;
if (t.s[i] == 1)
cout<<"FILL(1)\n";
else if (t.s[i] == 2)
cout<<"FILL(2)\n";
else if (t.s[i] == 3)
cout<<"DROP(1)\n";
else if (t.s[i] == 4)
cout<<"DROP(2)\n";
else if (t.s[i] == 5)
cout<<"POUR(1,2)\n";
else if (t.s[i] == 6)
cout<<"POUR(2,1)\n";
}
return 0;
}
for (i = 1; i <= 6; i++){
t1 = t;
if (i == 1)t1.x = a;//A装满;
else if (i == 2)t1.y = b;//B装满;
else if (i == 3)t1.x = 0;//A倒掉;
else if (i == 4)t1.y = 0;//B倒掉:
else if (i == 5){//A倒给B;
int k = t.x + t.y;
if (k <= b){
t1.x = 0;
t1.y = k;
}
else{
t1.x = k - b;
t1.y = b;
}
}
else if (i == 6){//B倒给A;
int k = t.x + t.y;
if (k <= a){
t1.x = k;
t1.y = 0;
}
else{
t1.x = a;
t1.y = k - a;
}
}
if (!vis[t1.x][t1.y]){
t1.step = t.step + 1;
vis[t1.x][t1.y] = 1;
t1.s[t1.step] = i;//记录操作;
Q.push(t1);
}
}
}
return -1;
}
int main()
{
while (cin>>a>>b>>c){
memset(vis, 0, sizeof vis);
if (bfs() == -1)
cout << "impossible\n";
}
return 0;
}
L - DNA sequence
思路:pos记录目前匹配位置,每次从0开始dfs搜索,dfs操作为当前位置匹配到ACGT之一,pos[i]+1
预期达到每个字符串当前的匹配位置都匹配不到ACGT任何一种(即所有字符串都到头了),
用d控制搜索层数,因为要控制最短序列,所以要逐步加深搜索层数,直到达到预期,
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n, d;
char c[10] = "ACGT";
struct node {
string s;
int l=0;
}a[10];
int pos[10];//记录第i个序列正在使用的位置
int fun() {
int ans = 0;
for (int i = 1; i <= n; i++) {
ans = max(ans, a[i].l - pos[i]);//当前情况下最长的未被匹配的长度
}
return ans;
}
int dfs(int p) {
if (p + fun() > d)//当前长度+估测长度>d返回0
return 0;
if (!fun()) //所有字符串到头了
return 1;
int t[10];
for (int i = 0; i < 4; i++) {
int flag = 0;
for (int j = 1; j <= n; j++) {
t[j] = pos[j];//方便回溯操作
}
for (int j = 1; j <= n; j++) {
if (a[j].s[pos[j]] == c[i]) {//如果这个位置匹配上c[i]字符,那么说明可以指向下一位字符了
flag = 1;
pos[j]++;
}
}
if (flag) {//如果此字符串有符合的字符,就继续往下搜
if (dfs(p + 1))
return 1;
for (int j = 1; j <= n; j++) { //回溯
pos[j] = t[j];
}
}
}
return 0;
}
int main() {
int t;
cin >> t;
while (t--) {
cin >> n;
d = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i].s;
a[i].l = a[i].s.size();
d = max(d, a[i].l);
pos[i] = 0;
}
while (1) {
if (dfs(0))
break;
d++;
}
cout << d << endl;
}
return 0;
}
K - Escape
思路:dfs,在dfs的过程中对于每个点需要在东南西北方向(这一点的行列)判断是否安全,找到朝此点方向的炮台,
若炮台与人的距离%子弹速度!=0,因为由题意小A不能在一秒钟中间停止,所以安全,
若人走的时间减去第一个子弹飞行到这个位置所需的时间为负数安全,若此时间%间隔=0,不安全
特殊情况,找到不是朝点方向炮台,此炮台会挡下后面所有子弹,此方向安全
标记需要添加时间这一维,对于某一时刻,人位于x,y的情况只需要访问一次
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int maxn = 110;
const int dir[5][2] = {{ 1,0} ,{-1, 0},{0,1},{0,-1},{0,0}}; // 可以不动
int n, m, k, d;
int map[maxn][maxn];
bool vis[maxn][maxn][maxn];
struct period {
char c;
int t, v;
}s[105][105];
struct node {
int x, y, step;
}t,t1;
void bfs()
{
queue<node> q;
int i, j, flag, dis, timee;
t.x = t.y = t.step = 0; //需要重置,方便后续数据使用
q.push(t);
vis[0][0][0] = true;
while (!q.empty())
{
t = q.front();
q.pop();
if (t.step > d) //体力耗尽
break;
if (t.x == n && t.y == m){ //到达return结束
cout << t.step << '\n';
return;
}
for (i = 0; i < 5; i++){
t1 = t;
t1.x += dir[i][0];
t1.y += dir[i][1];
t1.step++;
if(t1.x < 0 || t1.x > n || t1.y < 0 || t1.y > m)
continue;
if (!s[t1.x][t1.y].t && !vis[t1.x][t1.y][t1.step] && t1.step <= d){//枚举四个方向
flag = 1;
for (j = t1.x - 1; j >= 0; j--){//向北寻找朝南的炮台
if (s[j][t1.y].t && s[j][t1.y].c == 'S')
{
dis = t1.x - j;//距离
if (dis % s[j][t1.y].v)
break;//dis%v!=0,题意小A不能在一秒钟中间停止
timee = t1.step - dis / s[j][t1.y].v;//时间
if (timee < 0)
break;
if (timee % s[j][t1.y].t == 0){//时间%间隔=0,子弹刚好到这个点
flag = 0;
break;
}
}
if (s[j][t1.y].t)//找到不是朝南炮台,此炮台会挡下后面所有子弹,所以此方向安全
break;
}
if (!flag)
continue;
//其他方向
for (j = t1.x + 1; j <= n; j++)
{
if (s[j][t1.y].t && s[j][t1.y].c == 'N')
{
dis = j - t1.x;
if (dis % s[j][t1.y].v)
break;
timee = t1.step - dis / s[j][t1.y].v;
if (timee < 0)
break;
if (timee % s[j][t1.y].t == 0)
{
flag = 0;
break;
}
}
if (s[j][t1.y].t)
break;
}
if (!flag)
continue;
for (j = t1.y - 1; j >= 0; j--)
{
if (s[t1.x][j].t && s[t1.x][j].c == 'E')
{
dis = t1.y - j;
if (dis % s[t1.x][j].v)
break;
timee = t1.step - dis / s[t1.x][j].v;
if (timee < 0)
break;
if (timee % s[t1.x][j].t == 0)
{
flag = 0;
break;
}
}
if (s[t1.x][j].t)
break;
}
if (!flag)
continue;
for (j = t1.y + 1; j <= m; j++)
{
if (s[t1.x][j].t && s[t1.x][j].c == 'W')
{
dis = j - t1.y;
if (dis % s[t1.x][j].v)
break;
timee = t1.step - dis / s[t1.x][j].v;
if (timee < 0)
break;
if (timee % s[t1.x][j].t == 0)
{
flag = 0;
break;
}
}
if (s[t1.x][j].t)
break;
}
if (!flag)
continue;
vis[t1.x][t1.y][t1.step] = true;
q.push(t1);
}
}
}
cout << "Bad luck!\n";
}
int main() {
while (cin >> n >> m >> k >> d) {
memset(vis, 0, sizeof vis);
memset(s, 0, sizeof s);
for (int i = 0; i < k; ++i) {
char c; int t, v, x, y;
cin >> c >> t >> v >> x >> y;
s[x][y].c = c, s[x][y].t = t, s[x][y].v = v;
}
bfs();
}
return 0;
}
java学习日志(4)
7.java接口与实现
(1)java接口
通常使用关键字interface来定义一个接口,接口的定义分为接口声明和接口体
接口声明:interface 接口的名字
接口体:包含常量的声明和抽象方法两部分,接口体中只有抽象方法,而且接口体中所有的常量的访问权限一定都是public,而且是static常量,所有的抽象方法的访问权限一定都是public,例:
interface Printable {
public final static int MAX = 100; //等价写法:int MAX = 100;
public abstract void add(); //等价写法:void add();
public abstract float sum(float x,float y);
//等价写法:float sum(float x,float y);
}
注:修饰符public、final、static允许省略。
(2)java实现接口:
Java语言中,接口由类来实现,以便使用接口中的方法。一个类需要在类声明中使用关键字implements声明该类实现一个或多个接口。如果实现多个按口,用逗号隔开接口名。
例:
class A implements Printable,Addable //A类实现Printable和Addable接口
class Dog extends Animal implements Eatable,Sleepable
//Animal的Dog子类实现Eatable和Sleepable接口
如果一个非抽象类实现了某个接口,那么这个类必须重写这个接口中的所有方法。由于接口中的方法一定是public abstract方法,所以类在重写接口方法时不仅要去掉abstract修饰符,还要给出方法体,而且方法的访问权限一定要明显地用public来修饰。
例:
//Computable.java
public interface Computable {
int MAX = 46;
int f(int x);
}
//China.java
public class China implements Computable { //China类实现Computable接口
int number;
public int f(int x) { //不要忘记public关键字
int sum=0;
for(int i=1;i<=x;i++) {
sum=sum+i;
}
return sum;
}
}
//Japan.java
public class Japan implements Computable { //Japan类实现Computable接口
int number;
public int f(int x) {
return MAX+x;
}
}
//Test.java
public class Test {
public static void main(String args[]) {
China zhang;
Japan henlu;
zhang=new China();
henlu=new Japan();
zhang.number=32+Computable.MAX;
henlu.number=14+Computable.MAX;
System.out.println("zhang的学号"+zhang.number+",zhang求和结果"+zhang.f(100));
System.out.println("henlu的学号"+henlu.number+",henlu求和结果"+henlu.f(100));
}
}
程序可以用接口名访问接口中的常量,但是如果一个类实现了接口,那么该类可以直接在类体中使用该接口中的常量。
定义接口时,如果关键字interface前面加上public关键字,就称作public接口,public接口可以被任何一个类实现;如果不加上public关键字,就称作友好接口,友好接口可以被与该接口在同一包中的类实现。
如果父类实现了某个接口,那么子类也就自然而然地实现了该接口,也就是说,子类不必再使用关键字implements声明实现这个接口。
接口也是可以被继承的,即可以通过关键字extends声明一个接口是另一个接口的子接口。由于接口中的方法和常量都是共有的,所以子接口将继承父接口中的全部方法和常量。
注意:Java提供的接口都在相应的包中,通过import语句不仅可以引入包中的类,也可以引入包中的接口。
(3)Java接口回溯
接口属于引用型变量,接口变量中可以存放实现该接口的类的实例的引用,即存放对象的引用。
假设ImpleCom类是实现Com接口的类,用ImpleCom创建名字为object的对象,那么object对象不仅可以调用ImpleCom类中原有的方法,而且也可以调用ImpleCom类实现的接口方法。
ImpleCom object = new ImpleCom();
接口回调:
可以把实现某一接口的类创建的对象的引用赋值给该接口声明的接口变量,那么该接口变量就可以调用被类实现的接口方法,实际上,当接口变量调用被类实现的接口方法时,就是通知相应的对象调用这个方法。
例子:
interface ShowMessage {
void 显示商标(String s);
}
class TV implements ShowMessage {
public void 显示商标(String s) {
System.out.println(s);
}
}
class PC implements ShowMessage {
public void 显示商标(String s) {
System.out.println(s);
}
}
public class Example {
public static void main(String args[]) {
ShowMessage sm; //声明接口变量
sm=new TV(); //接口变量中存放对象的引用
sm.显示商标("长城牌电视机"); //接口回调。
sm=new PC(); //接口变量中存放对象的引用
sm.显示商标("联想奔月5008PC机"); //接口回调
}
}
**注意:接口无法调用类中的其他非接口方法。
(4)Java接口与多态:不同的类在实现同一个接口时可能具有不同的实现方式,所以接口变量在回调接口方法时就可能具有多种形态。
格式:
接口 变量名 = new 接口实现类();
(5)Java接口参数
如果一个方法的参数是接口类型,我们就可以将任何实现该接口的类的实例的引用传递给该接口参数,那么接口参数就可以回调类实现的接口方法。
例:
interface SpeakHello {
void speakHello();
}
class Chinese implements SpeakHello {
public void speakHello() {
System.out.println("中国人习惯问候语:你好,吃饭了吗? ");
}
}
class English implements SpeakHello {
public void speakHello() {
System.out.println("英国人习惯问候语:你好,天气不错!");
}
}
class KindHello {
public void lookHello(SpeakHello a) { //接口类型参数
a.speakHello(); //接口回调
}
}
public class Main {
public static void main(String args[]) {
KindHello kindHello = new KindHello();
kindHello.lookHello(new Chinese());
kindHello.lookHello(new English());
}
}
(6)java接口与abstract比较
1). abstract类 和接口都可以有abstract方法。
2).接口中只可以有常量,不能有变量;而abstract类中即可以有常量也可以有变量。
3). abstract类 中也可以有非abstract方法,接口不可以。(JavaJDK1.8之后不成立)
(7)java接口的uml图
使用一个长方形描述一个接口的主要构成,将长方形垂直地分为三层。
顶部第1层是名字层,接口的名字必须是斜体字形,而且需要用<<interface>>修饰名字,并且该修饰和名字分列在两行。
第2层是常量层,列出接口中的常量及类型,格式是“常量名字:类型”。
第3层是方法层,也称操作层,列出接口中的方法及返回类型,格式是“方法名字(参数列表):类型”。
如果一个类实现了一个接口,那么类和接口的关系是实现关系,称类实现接口。UML通过使用虚线连接类和它所实现的接口,虚线起始端是类,虚线的终点端是它实现的接口,但终点端使用一个空心的三角形表示虚线的结束。
8.java内部类与异常类
(1)Java内部类
在一个类中定义另一个类,我们把这样的类称作内部类,包含内部类的类称作内部类的外嵌类。
内部类与外部类关系:
1)内部类的外嵌类的成员变量在内部类中仍然有效,内部类中的方法也可以调用外嵌类中的方法。
2)内部类的类体中不可以声明类变量和类方法,外嵌类的类体中可以用内部类声明对象作为外嵌类的成员。
3)内部类仅供它的外嵌类使用,其他类不可以用某个类的内部类声明对象。
注意:Java编译器生成的内部类的字节码文件的名字和平常的类的名字不同,内部类对应的字节码文件的名字格式是“外嵌类名$内部类名”。
内部类可以被修饰为static内部类,static内部类是外嵌类中的一种静态数据类型,程序可以在其他类中使用static内部类来创建对象,但是,static内部类不能操作外嵌类中的实例成员变量。
例:
class RedCowForm {
static String formName;
RedCow cow; //内部类声明对象
RedCowForm(String s){
cow = new RedCow(88,66,2000);
formName = s;
}
public void showCowMess() {
cow.speak();
}
class RedCow{ //内部类的声明
String cowName = "小牛";
int height,weight,price;
RedCow(int h,int w,int p) {
height = h;
weight = w;
price = p;
}
void speak() {
System.out.println("我是"+cowName+",身高"+height+"cm,体重"+weight+"kg,生活在"+formName);
}
}
}
public class Main {
public static void main(String[] args) {
RedCowForm form = new RedCowForm("dotcpp农场");
form.showCowMess();
form.cow.speak();
}
}
(2)Java匿名类
匿名类就是不能有名字的类,它们不能被引用,只能在创建时用new语句来声明它们。
使用匿名内部类我们必须要继承一个父类或者实现一个接口。
1)匿名内部类中是不能定义构造函数的。
2)匿名内部类中不能存在任何的静态成员变量和静态方法。
3)匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
格式如下:
class outerClass {
// 定义一个匿名类
object1 = new Type(parameterList) {
// 匿名类代码
};
}
注意:因为匿名类是表达式形式定义的,所以末尾以分号;来结束。
匿名类继承接口/父类
/*interface Dotcpp {
public void display();
}*/
class Dotcpp {
public void display() {
System.out.println("在Dotcpp类内部");
}
}
class AnonymousDemo {
public void createClass() {
//创建的匿名类继承了Dotcpp类
Dotcpp d1 = new Dotcpp() {
public void display() {
System.out.println("在匿名类内部");
}
};
d1.display();
}
}
class Main {
public static void main(String[] args) {
AnonymousDemo an = new AnonymousDemo();
an.createClass();
}
}
(3)Java异常类
Java使用throw关键字抛出一个Exception子类的实例表示异常发生。
例如:java.lang包中的Integer类调用其类方法public static int parseInt(String s)可以将“数字”格式的字符串,比如“123456”,转化为int型数据,但是,当试图将字符串“dot123”转换成数字时,代码如下:
int number = Integer.parseInt("dot123");
方法parseInt()在执行过程中就会使用throw关键字抛出一个NumberFormatException对象,此时也就意味着程序运行出现NumberFormatException异常。
Java允许定义方法时声明该方法调用过程中可能出现的异常,即允许方法调用过程中抛出异常对象,终止当前方法的继续执行。
异常对象可以调用如下方法得到或输出有关异常的信息:
public String getMessage(); //得到异常的详细信息
public void printStackTrace(); //得到堆栈跟踪输出(企业一般不允许输出此项,可以得到异常的全部信息)
public String toString(); //以文本形式表示以上信息
getCause(); //得到异常原因
(4)java异常的分类
1)Throwable类 :所有错误或异常的超类,是对所有异常进行整合的一个普通类,其作用是提取保存在堆栈中的错误信息。
2)Error类 :Throwable类的子类,包括系统异常、虚拟机异常等用户无法阻止的问题。
3)Exception类 :其他因编程错误或偶然的外在因素导致的一般性问题
Exception类也是Throwable类的子类,它是程序本身可以处理的异常,分为运行时异常(checked)和非运行时异常(unchecked)。其中运行时异常可处理,也可不处理;非运行时异常必须处理。
(5)常见异常
1) NullPointerException(空指针异常)
调用了未经初始化的对象或者是不存在的对象,数组的初始化是对数组分配需要的空间,而初始化后的数组,其中的元素并没有实例化,依然是空的,所以如果要调用的话,需要对每个元素都进行初始化。
2)ClassCastException(类转换异常)
数据类型转换错误比如:String temp="abc";如果设为int temp就会报错
3). IndexOutOfBoundsException(数组下标越界异常)
4) IllegalAccessException(访问权限异常)
当应用程序要调用一个类,但当前的方法对该类没有访问权限便会出现该异常,在程序中使用package的情况下要注意这个异常。
5)IOException(输入输出异常)
一般读写文件会出现这个异常,比如你想从磁盘上读一个文件到你写的程序,如果硬盘上没有这文件,Java虚拟机就会报这个异常。
(6)Java处理异常
try…catch
将可能出现的异常操作放在try…catch语句的try部分,一旦try部分抛出异常对象,或调用某个可能抛出异常对象的方法,并且该方法抛出了异常对象,那么try部分将立刻结束执行,转向执行相应的catch部分。所以,程序可以将发生异常后的处理放在catch部分。
try…catch语句可以由几个catch组成,分别处理发生的相应异常。
格式:
try {
//包含可能发生异常的语句
}
catch(ExceptionSubClass1 e) {
…
}
catch(ExceptionSubClass2 e) {
…
}
注:各个catch参数中的异常类都是Exceptin的某个子类,表明try部分可能发生的异常,这些子类之间不能有父子关系,
例:
编写一个录入学生姓名、年龄和性别的程序,要求能捕捉年龄不为数字时的异常。在这里使用 try catch 语句来实现,具体代码如下:
import java.util.Scanner;
public class Test02 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("---------学生信息录入---------------");
String name = ""; // 获取学生姓名
int age = 0; // 获取学生年龄
String sex = ""; // 获取学生性别
try {
System.out.println("请输入学生姓名:");
name = scanner.next();
System.out.println("请输入学生年龄:");
age = scanner.nextInt();
System.out.println("请输入学生性别:");
sex = scanner.next();
} catch (Exception e) {
e.printStackTrace(); //得到堆栈跟踪输出(企业一般不允许输出此项,可以得到异常的全部信息)
System.out.println("输入有误!");
}
System.out.println("姓名:" + name);
System.out.println("年龄:" + age);
}
}
try…catch…finally语句的格式如下:
try {
//包含可能发生异常的语句
}
catch(异常类名 异常对象) {
//异常处理的代码
}
finally {
//一定执行的代码
}
try…catch…finally
格式: